Finding a good solution for sharing files between Linux users is a nightmare.
If using a unique UID is not a problem, it’s the most simple solution. All clients access files with the same UID. This way you cannot know who does what, and users cannot fine tune access rights.
The problem: default umask is ALWAYS 0022, so that any created file will get rw– r–– r–– permissions. Only the owner can write. Nobody else. To share files, a group must have write access.
You can change the umask. For command line, you set it in .bashrc or .profile, or /etc/profile for all users. For a SFTP share, you can set it with a trick. For Apache HTTP server, you can set it with /etc/apache2/envvars under Debian.
If file sharing is only done via on service, changing umask is simple, otherwise it’s not that easy. And even if you change umask for all services, nothing is perfect: for example it doesn’t work with Nautilus and SFTP. Some clients drop files and issue a chmod right after: the hell. You can also try the power of POSIX ACL to force permissions. But problems still remain with some clients.
And for the umask, maybe you don’t want all files to be dropped group writable. Maybe you want more granularity on permissions.
So I abandonned the idea of fixing the problem at the source in favor of some trick AFTER file creation.
The most simple solution is the cron task: every X minutes, run chmod -R g+w on the directory. This way permissions are not fixed immediately, but asynchronously. And it adds a (very) little more load to your system.
My solution uses inotify to listen for file changes and force permissions when files are created:
aptitude install inotify-tools
And the magical command:
inotifywait -mrq -e CREATE --format %w%f /tmp/mytest/ | while read FILE; do chmod g=u "$FILE"; done
UPDATE 2010-10-30
To support spaces at the end of filenames, and backslashes, use:
inotifywait -mrq -e CREATE --format %w%f /tmp/mytest/ | while IFS= read -r FILE; do chmod g=u "$FILE"; done
Thanks to vitoreiji (see comments)
inotifywait listens for events in the /tmp/mytest directory. When a file is created, it’s displayed on standard output. Then each fileline is read by the while loop and permissions are changed. g=u gives the group the user’s permissions (with g+w, if the user drops a file with rw– ––– –––, permissions will be rw– –w– –––).
You can now test file/directory creation and copy. mkdir -p a/b/c/d/e shoud also work.
Finally, add it in a boot script:
vi /usr/local/bin/inotifywait.sh && chmod +x /usr/local/bin/inotifywait.sh #!/bin/sh # Take the directory name as argument inotifywait -mrq -e CREATE --format %w%f "$1" | while read FILE do chmod g=u "$FILE" done
vi /etc/init.d/inotifywait.sh && chmod +x /etc/init.d/inotifywait.sh #! /bin/sh case "$1" in start|"") rm -f /tmp/inotifywait.log /usr/local/bin/inotifywait.sh /path/to/dir/ >/tmp/inotifywait.log 2>&1 & ;; restart|reload|force-reload) echo "Error: argument '$1' not supported" >&2 exit 3 ;; stop) # killall inotifywait ??? ;; *) echo "Usage: inotifywait.sh [start|stop]" >&2 exit 3 ;; esac :
(Debian way)
update-rc.d inotifywait.sh defaults
Note: a drawback: there is a limit on the number of tracked files. See -r option in man inotifywait.
Then the final touch in order for the new files to be created with the same group as their parent: setgid bit for all directories.
find /path/to/dir -type d -exec chmod g+s {} \;
Links:
man inotifywait- inotify-tools
- http://en.wikipedia.org/wiki/Inotify



I think you would get something simpler if you were using incron (cron, but based on inotify events).
Hello,
Yes, in fact I tried incron before, but never managed to make it work :
My incron task was never taken in account. I searched for some log or debug in the daemon with no success.
Maybe I should try it again.
Hello,
Est-ce qu’une solution plus classique n’est pas
– de regrouper les utilisateurs dans un groupe (le plus souvent users);
– de créer des répertoires partagés appartenant au groupe en question, mais dotés du sticky bit pour le groupe ?
Soit
# chown foo:users /home/share
# chmod g+s /home/share
bar$ touch /home/share/test
bar$ ls -l /home/share
total 0
-rw-rw-r– 1 bar users 0 23 oct 16:28 test
ce qui force les fichiers de ce répertoire à adopter l’id du groupe, quel qu’en soit le créateur.
À la suite de quoi, le contenu de ce répertoire peut être manipulé (effacé, modifié…) par les membres du groupe ?
Bonjour,
Attention, le sticky bit est tout autre chose (on le met avec le ‘t’ de la commande chmod. Il est utilisé pour le /tmp par exemple).
Tu veux parler du bit setuid. Je le mentionne à la toute fin de l’article. C’est certes très utile pour conserver le groupe à la création de fichiers.
Dans ton exemple, le fichier créé récupère les droits d’écriture pour le groupe. Ou bien tu a changé ton umask, ou bien tu as utilisé la technique inotifywait, mais ça n’est pas le bit setuid qui a mis les droits d’écriture au groupe.
Marc
Pour le sticky bit, effectivement, j’ai confondu les dénominations.
Pour le reste, au cas où je m’étais emmêlé les pinceaux, je revérifie:
comme root
fantasio:~# mkdir /home/share
fantasio:~# ls -dl /home/share
drwxr-xr-x 2 root root 4096 oct 24 10:05 /home/share
fantasio:~# chown root:users /home/share
fantasio:~# chmod g+s /home/share
fantasio:~# ls -dl /home/share
drwxr-sr-x 2 root users 4096 oct 24 10:05 /home/share
fantasio:~# chmod g+w /home/share
fantasio:~# ls -dl /home/share
drwxrwsr-x 2 root users 4096 oct 24 10:05 /home/share
Comme utilisateur ordinaire membre de users:
gv@fantasio:/home/share$ umask
0033
gv@fantasio:/home/share$ touch foo
gv@fantasio:/home/share$ ls -l foo
-rw-r–r– 1 gv users 0 oct 24 10:18 foo
gv@fantasio:/home/share$ umask 002
gv@fantasio:/home/share$ touch bar
gv@fantasio:/home/share$ ls -l
total 0
-rw-rw-r– 1 gv users 0 oct 24 10:19 bar
-rw-r–r– 1 gv users 0 oct 24 10:18 foo
Comme tu le vois, le masque standard crée des fichiers accessibles seulement en écriture pour le propriétaire, mais une fois modifié, il permet l’écriture aux membres du groupe.
Dans les mêmes conditions, un autre utilisateur du groupe peut créer un fichier:
do@fantasio:/home/share$ touch foobar
do@fantasio:/home/share$ ls -l
total 0
-rw-rw-r– 1 gv users 0 oct 24 10:19 bar
-rw-r–r– 1 gv users 0 oct 24 10:18 foo
-rw-rw-r– 1 do users 0 oct 24 10:20 foobar
dans lequel je peux écrire:
gv@fantasio:/home/share$ ls > foobar
gv@fantasio:/home/share$ ls -l
total 4
-rw-r–r– 1 gv users 0 oct 24 10:18 foo
-rw-rw-r– 1 do users 22 oct 24 10:21 foobar
gv@fantasio:/home/share$ rm -f foobar
gv@fantasio:/home/share$ exit
Ça me paraît donc une stratégie suffisante quoique limitée pour travailler à plusieurs sur des fichiers. Mais je reconnais que n’ayant jamais géré que des machines uni-personnelles, je n’ai jamais été confronté à une mise en pratique, donc…
Cordialement
D’accord d’accord, ta démonstration montre comment partager des fichiers, mais en modifiant le umask.
Dans ce que tu montres, l’utilisateur ‘do’ a bien un umask de 002, qui n’est pas l’umask par défaut 022 présent sur la majorité des distribution Linux.
Bien sûr qu’en changeant le umask on peut partager les fichiers.
Mais mon article explique justement comment le faire SANS modifier le umask. Car modifier le umask pose certains problèmes.
Loin de moi l’idée de polémiquer ;)
Je crois quand même me souvenir qu’il y a une bonne dizaine d’année, les distribs étaient configurées pour que tout nouvel utilisateur appartienne au groupe users. Je me demande si ça n’est pas la Redhat ou Mandrake qui a introduit les groupes uni-personnels qui permettaient précisément d’avoir comme umask 0022 tout en préservant la confidentialité des fichiers personnels (puisque le groupe autorisant l’accès en écriture était restreint à ce même utilisateur).
Si tu avais des références sur les problèmes caussés par les changements d’umask, ça m’intéresserait.
Cordialement,
Great tip, but this is not gonna work if you create a file with a newline in its name (although I am very contrary to these bizarre filenames).
Don’t know how to fix it, though, I don’t see a way to use a custom file separator in inotifywait’s output.
Any ideas?
Thanks for your comment. There is a bug in my command.
The command works fine with spaces in filenames, but when spaces are at the end, it fails.
when issuing:
touch ‘/tmp/mytest/ a b’
then:
touch ‘/tmp/mytest/ c ‘
# inotifywait -mrq -e CREATE –format %w%f /tmp/mytest/ | while read FILE; do chmod g=u “$FILE”; echo “$FILE”; done
/tmp/mytest/ a b
chmod: cannot access `/tmp/mytest/ c’: No such file or directory
/tmp/mytest/ c
The fix is documented here:
http://wiki.bash-hackers.org/commands/builtin/read#portability_considerations
I also updated my post. Thanks.
Note that it also fails with backslashes in filenames.
The -r option of read solves this problem.
The IFS= solves the whitespace problem.
You gave great points here. I found nearly all people agree with your blog.
Salut,
Sujet très intéressant que inotify!
Sais-tu qu’il existe un programme nommé incron qui utilise inotify et facilite ce que tu fais ?
En fait je l’ai testé mais je n’ai pas réussi à le faire fonctionner.
Tu l’as testé toi ?
Oui en effet, je l’ai testé et je l’utilise.
J’ai utilisé ce tuto http://dmesg.fr/categorie-logiciels/75-incron-executer-des-actions-selon-des-evenements-du-systeme-de-fichiers que je conseille car il est assez bien fait.
Je n’ai par contre pas utilisé incron pour faire la même chose que dans le tuto : en fait, quand un fichier multimedia est téléchargé (et donc créé), je change le groupe en “media” et je donne comme toi le droit en écriture au groupe.
Pour résumer, tu installes incron :
aptitude install incron
Soit tu édites les tables dans /etc/incron.d/ manuellement (à toi de les créer lors de la première utilisation, tu peux mettre les noms que tu veux). Ces tables seront les table “system”.
Soit tu utilises “incrontab -e” pour éditer une table qui se trouve dans /var/… (je ne sais plus) et qui porte le nom de l’utilisateur qui a lancé la commande. Ces tables seront les tables “user”. root peut créer une table de cette façon aussi.
Pour les différentes actions, regarde dans le tuto ce sera mieux expliqué :)
« le bit setuid au groupe pour tous les dossiers.
find /path/to/dir -type d -exec chmod g+s {} \; » :
La commande indiquée fixe le bit SGID, et non pas le bit SUID ;)
Merci ! j’ai corrigé la coquille.
Nah, incron won’t be any use because it doesn’t watch subdirectories. inotifywait is the way to go, thanks.
It’s kinda pathetic that it’s needed, though. I tried the umask and acl routes, but they both fail one way or the other with samba, netatalk and/or sftp.
Thanks for the post
For Centos you need to add after first line:
###
# chkconfig: 2345 98 55
# description: Manages the services you are controlling with the
chkconfig command
###
and then:
chkconfig –add inotifywait.sh
Nice script, but most clients will first transfer the file and then try to set the correct permisions so only watching for create will not do the trick.
@Richard
You can use -e move -e attrib to avoid this problem
-e move : check the files moved or renamed
-e attrib : check the files which permissions where changed
@Rchard and myself
If using -e attrib, it loops.
chmod = modification of attrib => chmod…
I encountered the same problem with ATTRIB event’s
I had trouble getting this to work when Using Nautilus to transfer files over to an NFS share.
It would only work when creating new Folders, but not when copying/moving.
So I did some digging and here is the command that works (note that I have not used the one for trailing spaces or backstlashes:
****************************************
inotifywait -mrq -e CREATE -e CLOSE -e MOVED_TO –format %w%f /home/alex/autochown/ | while read FILE; do `sleep 1` && chmod g=u “$FILE”; done
****************************************
Explanations of modifications:
-e CLOSE and -e MOVED_TO are the last events that Nautilus does when creating new files or copying/moving files around. This way, permissions are set after Nautilus finished the copy.
the “sleep 1” is because when Nautilus creates a file, it changes the permissions right away. The issue is that the NFS server machine will get into a “race condition” and will change the permissions a few milliseconds before Nautilus will (all this happens in less than 0.5 seconds).
To work around this, we add a 1 second delay to the chmod command. This might only be a problem is the rate at which files are created in the folder is more than 1 per second for a very long time.
dear Alecz!
Nautilus is working on his own cunning scenario – I am agree with this. But sleep 1 – this is not a good solution, because we get timeout for each file/dir for a 1 second (as is in you example).
This is a very significant reduction in performance.