Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I'd like to clone all the ZFS ACLs from one file to another.

With POSIX ACLs this can be done by piping the output of getfacl to setfacl.

Is there an easy and quick way to do this with the NFSv4 style ACLs in ZFS? I know I can read off the output of ls -lV and then enter it as part of a chmod, but I can't seem to find a functional equivalent to the POSIX way of copying ACLs.

share|improve this question

4 Answers 4

up vote 2 down vote accepted

Instead of using ls -lV you can use ls -lv which can be fed in to a script to convert it to a sequence of chmod commands to duplicate the ACLs.

E.g. if the ACl looks like this:

$ ls -lv file1
     0:owner@::deny
     1:owner@:read_data/write_data/append_data/write_xattr/execute
         /write_attributes/write_acl/write_owner:allow
     2:group@:read_data/write_data/append_data:deny
     3:group@:execute:allow
     4:everyone@:read_data/write_data/append_data/write_xattr
        /write_attributes/write_acl/write_owner:deny
     5:everyone@:read_xattr/execute/read_attributes/read_acl/synchronize:allow

It should be turned in to the following sequence of chmod commands:

chmod A0=owner@::deny file2
chmod A1=owner@:read_data/write_data/append_data/write_xattr/execute/write_attributes/write_acl/write_owner:allow file2
chmod A2=group@:read_data/write_data/append_data:deny file2
chmod A3=group@:execute:allow file2
chmod A4=everyone@:read_data/write_data/append_data/write_xattr/write_attributes/write_acl/write_owner:deny file2
chmod A5=everyone@:read_xattr/execute/read_attributes/read_acl/synchronize:allow file2

I recently found myself in a situation where a script like the one described above would have been useful, so here's a little Bash script I made (can also be sourced to the shell and run as a function) to print the list of chmod commands necessary to copy the ZFS ACLs from one file to another:

#!/bin/bash

acl_as_chmods () {
# print list of chmod commands to copy the ACL entries from '$1' to '$2'
 [[ -a "$1" ]] 2>/dev/null || {
   echo "Valid reference file required." >&2
   return 1
 }
 ls -vd "$1" | {
   read # first line is not ACL information; bypass
   read ACL_entry
     echo -n "chmod A=${ACL_entry#*:}"
   # if no target file was specified as '$2', use 'TARGET' variable at runtime
   while read ACL_entry || { echo " ${2-\$TARGET}"; false; }
   do
     [[ "$ACL_entry" == [0-9]*:* ]] && \
       echo -en " ${2-\$TARGET}\nchmod A${ACL_entry%%:*}+${ACL_entry#*:}" || \
       echo -n "$ACL_entry"
   done
 }
}

## run as script or source function to shell?
__acl_as_chmods () {
 [[ "${FUNCNAME[1]}" == "source" ]] || acl_as_chmods "$@"
}

__acl_as_chmods "$@"

Here are a couple example usages and their output for file1 from above:

~$ ./acl_as_chmods.sh file1 file2
chmod A=owner@::deny file2
chmod A1+owner@:read_data/write_data/append_data/write_xattr/execute/write_attributes/write_acl/write_owner:allow file2
chmod A2+group@:read_data/write_data/append_data:deny file2
chmod A3+group@:execute:allow file2
chmod A4+everyone@:read_data/write_data/append_data/write_xattr/write_attributes/write_acl/write_owner:deny file2
chmod A5+everyone@:read_xattr/execute/read_attributes/read_acl/synchronize:allow file2

~$ source acl_as_chmods.sh
~$ acl_as_chmods file1
chmod A=owner@::deny $TARGET
chmod A1+owner@:read_data/write_data/append_data/write_xattr/execute/write_attributes/write_acl/write_owner:allow $TARGET
chmod A2+group@:read_data/write_data/append_data:deny $TARGET
chmod A3+group@:execute:allow $TARGET
chmod A4+everyone@:read_data/write_data/append_data/write_xattr/write_attributes/write_acl/write_owner:deny $TARGET
chmod A5+everyone@:read_xattr/execute/read_attributes/read_acl/synchronize:allow $TARGET

If we like, we can even evaluate these chmods directly, if both files are accessible on this host and we wish to copy the ACLs from file1 to file2 immediately:

~$ ls -Vd file*  #BEFORE
-rwx--x--x   1 user user       0 Jun 19 04:12 file1
            owner@:--------------:------:deny
            owner@:rwxp---A-W-Co-:------:allow
            group@:rw-p----------:------:deny
            group@:--x-----------:------:allow
         everyone@:rw-p---A-W-Co-:------:deny
         everyone@:--x---a-R-c--s:------:allow
---x------+  1 user user       0 Jun 19 04:12 file2
            owner@:--x-----------:------:allow

~$ eval "$(acl_as_chmods file1 file2)"

~$ ls -Vd file*  #AFTER 
-rwx--x--x   1 user user       0 Jun 19 04:12 file1
            owner@:--------------:------:deny
            owner@:rwxp---A-W-Co-:------:allow
            group@:rw-p----------:------:deny
            group@:--x-----------:------:allow
         everyone@:rw-p---A-W-Co-:------:deny
         everyone@:--x---a-R-c--s:------:allow
-rwx--x--x   1 user user       0 Jun 19 04:12 file2
            owner@:--------------:------:deny
            owner@:rwxp---A-W-Co-:------:allow
            group@:rw-p----------:------:deny
            group@:--x-----------:------:allow
         everyone@:rw-p---A-W-Co-:------:deny
         everyone@:--x---a-R-c--s:------:allow
share|improve this answer
    
This is probably the closest I'll get. I'll look at automating it somehow as dlamblin suggests. –  Kamil Kisiel Aug 11 '09 at 0:35

Taking what Martin suggests and applying a bit of perl you might get something like:

#!/usr/bin/perl
use warnings;
use strict;

my $state = 0;
if ($#ARGV < 1 || $#ARGV > 2) {
  print "\n\tUsage: $0 srcFile destFile\n";
}
my $acl=`ls -lv "${ARGV[0]}"`;
my @out="chmod", "arg", $ARGV[1];
foreach my $line ($acl) {
  chomp $line;
  if ($line =~ m/^\s+(\d+):(.*)$/) {
    if ($state > 0) {
      print join(" ",@out)."\n";
      #system(@out) or die "system @args failed: $?";
    }
    $state = 1;
    $out[1] = "A$1=$2";
  } else {
    $line =~ m/^\s+(.*)$/;
    $state = 2;
    $out[1] .= $1;
  }
}
if ($state > 0) {
  print join(" ",@out)."\n";
  #system(@out) or die "system @args failed: $?";
}

try it before uncommenting the system(@out) lines.

share|improve this answer

I ran into this same issue. My code is here:

https://gist.github.com/1021032

Works like a charm for me. Hope it's handy if anybody else out there ever runs into this problem.

share|improve this answer

Annoyingly, this doesn't seem to be properly exposed at the shell level. In C, there are functions acl_get(3SEC) and acl_set(3SEC) which can be used to take the ACL from one file and apply it to another (among other options, obviously). They will also, usefully, translate an ACL from POSIX-draft to NFSv4 type; this is what system commands like mv and cp use.

I wrote, at one time, a command to copy an acl from a source to a destination file using this technique, but I presently cannot find the source code. Should I find it, I'll append it to this answer.

share|improve this answer
    
Sounds nice. Would definitely like to see that. –  Kamil Kisiel Sep 17 '09 at 21:29

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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