5

I struggled with this problem on FreeBSD recently, but thank God for ZFS which solved it for me there. However I have it again in CentOS with ext4 and don't know if there is an easy way around it (or any way around it). What I want is a directory in which any user in a certain group has guaranteed read/write access to the files, regardless of clueless users' umasks, poor FTP client upload decisions, etc.. I don't think it's possible, but I'd like to be wrong. It looks like the reason it's not possible is that ext4 ACLs cannot override file permissions, only intersect with them. For example:

# mkdir bar
# chmod 700 bar
# getfacl bar
# file: bar
# owner: root
# group: mygroup
# flags: -s-
user::rwx
group::rwx                      #effective:---
group:mygroup:rwx              #effective:---
mask::---
other::---
default:user::rwx
default:group::rwx
default:group:mygroup:rwx
default:mask::rwx
default:other::---

You can see that the default ACL and mask both specify rwx for mygroup but the file permissions trump that and result in ---. Unfortunately that means if a user's FTP client (for example) uploads files as 640, others in the group wouldn't be able to mess with it. Is there a way around this?

  • The mask above is --- not rwx. – ctrl-alt-delor Jun 4 '16 at 20:52
4

The permissions granted by an ACL are additive, but perhaps you're expecting them to be recursive? (they aren't)

You can almost get what you want with ACLs. You need to start out by setting the ACL like above recursively on every file and directory in the tree. Be sure to include the default:group:mygroup:rwx setting on directories. Now, any new directory will get those settings automatically applied to it, and and new file in those directories likewise.

There are two times when this still fails:

  • when someone moves a file or directory from outside the tree. Since the inode already exists, it won't get the defaults set on it.
  • when someone extracts files from an archive using an ACL-aware program which overwrites the defaults.

I don't know any way to handle those two other than writing a cron job to periodically run chgrp -R mygroup DIRECTORY; chmod g+rwx -R DIRECTORY. This may or may not be practical depending on the number of files in your shared directory.

Here's a slightly modified version of a script I use to fix ACLs on a tree of files. It completely overwrites any ACLs on anything in the tree with a specific list of read-write groups and read-only groups.

#! /usr/bin/env perl
use strict;
use warnings;
use String::ShellQuote;
use Cwd 'abs_path';

# Usage: fix-permissions.pl DIRECTORY RW_GROUP1,RW_GROUP2... RO_GROUP1,RO_GROUP2...

my $dir= $ARGV[0];
my @rw_groups= split ',', $ARGV[1] if $ARGV[1];
my @ro_groups= split ',', $ARGV[2] if $ARGV[2];
-d $dir or die "No such directory'$dir'\n";
$dir= abs_path($dir);
$dir =~ m|/[^/]+/| or die "Cowardly refusing to run on a top-level directory: $dir\n";

# Give all files rw-r----- and all directories rwxr-x---
# then give each rw_group read/write access, then each ro_group
# read-only access to the whole tree
my $dir_perm= join(',',
  'u::rwx',
  'g::r-x',
  'o::---',
  'd:u::rwx',
  'd:g::r-x',
  'd:o::---',
  ( map { "g:$_:rwx" } @rw_groups ),
  ( map { "d:g:$_:rwx" } @rw_groups ),
  ( map { "g:$_:r-x" } @ro_groups ),
  ( map { "d:g:$_:r-x" } @ro_groups )
);
my $file_perm= join(',',
  'u::rwx',
  'g::r-x',
  'o::---',
  ( map { "g:$_:rw-" } @rw_groups ),
  ( map { "g:$_:r--" } @ro_groups )
);

for (
  "find ".shell_quote($dir)." -type d -print0 | xargs -0 -r setfacl --set ".shell_quote($dir_perm),
  "find ".shell_quote($dir)." ! -type d -print0 | xargs -0 -r setfacl --set ".shell_quote($file_perm)
) {
  0 == system($_) or die "command failed: $_\n";
}
  • Okay so another vote for "it's not possible". I already have the defaults set, you can see that in the default ACLs and s flag. I could easily (for this case) set up a cron job to chown/chmod the files with the standard unix group, but was hoping to avoid that. The second failure case you mention is basically what I want to prevent. :) I would probably try some sort of inotify route instead of cron. I'm not sure I would call it recursive, maybe reversed, or ignored. ZFS calls it passthrough. – Josh Feb 2 '15 at 13:33
  • If you have to do the cron solution, then consider instead inotify, it will wait for a change, and then run the script. (instead of a timer) – ctrl-alt-delor Jun 4 '16 at 20:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.