2010-10-22 00:29

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:

2010-10-22 00:29 · Tags: , , , ,