How do I clone ZFS ACLs from one file to another?
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
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.
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.
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.