How to recursively set NFSv4 ACLs in a GPFS filesystem
Summary
There is no "recursive" option on the mmputacl command, and it is a fairly common request of administrators to be able to recursively set ACLs for existing files and directories. Often, the administrator has added inheritance flags at the root directory of the export, but that will only cause new files and new directories to inherit the ACE. To modify the ACLs of the existing files and directories requires calling mmputacl for each file.
Objective
The purpose of this document is to demonstrate some example scenarios where one might want to update ACLs recursively and to provide some sample shell scripts and commands for doing so. Certainly other scripting languages like perl, python or awk could be used, and this how-to document is meant to be used as a starting point for developing scripts to work in a particular environment.
This article assumes that the reader is familiar with ACLs and the corresponding information in the Spectrum Scale knowledge center. Please also note that additional limitations apply for Microsoft Windows:
https://www.ibm.com/support/knowledgecenter/STXKQY_5.0.0/com.ibm.spectr…
Important
Themmgetacl,mmputacl, and mmeditacl commands are available to change the ACLs directly. As the SMB clients might depend on the order of entries in the ACL, it is not recommended to change the ACLs directly on GPFS™ while using the SMB protocol. Changing an ACL directly in GPFS also does not account for inherited entries. So, it is recommended to change the ACLs from a windows client.
Steps
Let us start with a test directory, testdir1, and files and subdirectories within testdir1. This example assumes that only NFSv4 ACLs are used in the file system.
[root@node1 testdir1]# mmlsfs gpfs1 -k -T flag value description ------------------- ------------------------ ----------------------------------- -k nfs4 ACL semantics in effect -T /ibm/gpfs1 Default mount point [root@node1 testdir1]# pwd /ibm/gpfs1/testdir1 [root@node1 testdir1]# ls -la total 259 drwxr-xr-x. 4 testuser1 testuser1 4096 Jul 26 10:06 . drwxr-xr-x. 3 root root 262144 Jul 26 10:03 .. -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:04 testfile1 -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:05 testfile2 drwxr-xr-x. 3 testuser1 testuser1 4096 Jul 26 10:07 testsubdir1 drwxr-xr-x. 2 testuser1 testuser1 4096 Jul 26 10:06 testsubdir2 [root@node1 testdir1]# mmgetacl . #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED special:group@:r-x-:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED special:everyone@:r-x-:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
We start out with a set of "default" ACLs on these files. The ACLs are determined by the umask when the file is created. We want to change the ACLs to:
- Remove access for "special:group" and "everyone" - the equivalent of a 700 or 600 POSIX permission.
- Add read-only access for the group "users"
- Have new files and directories inherit the ACL from the parent directory.
We start by setting the ACL on the parent directory, /ibm/gpfs1/testdir1. Spectrum Scale gives us two options for this: we can either use mmeditacl or mmputacl. For this example, we'll use mmputacl. The mmputacl command takes a file containing the ACL as input. First, we'll generate such an ACL file using the mmgetacl command:
[root@node1 testdir1]# mmgetacl . > /tmp/testdir1.acl.txt [root@node1 testdir1]# cat /tmp/testdir1.acl.txt #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED special:group@:r-x-:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED special:everyone@:r-x-:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
This is the original ACL for testdir1. Now, we'll make our changes, leaving us with the new ACL for testdir1.
[root@node1 testdir1]# vi /tmp/testdir1.acl.txt [root@node1 testdir1]# cat /tmp/testdir1.acl.txt #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow:FileInherit:DirInherit (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED group:users:r-x-:allow:FileInherit:DirInherit (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
This file is now our input file to the mmputacl command.
[root@node1 testdir1]# mmputacl . -i /tmp/testdir1.acl.txt [root@node1 testdir1]# mmgetacl . #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow:FileInherit:DirInherit (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED group:users:r-x-:allow:FileInherit:DirInherit (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
The POSIX mode bits on the current directory are also changed to reflect the ACL change, but the permissions and ACL of the existing files and subdirectories have not changed. mmputacl only operates on a single file or directory; it cannot be used to recursively set the ACL.
[root@node1 testdir1]# ls -la total 259 drwx------. 4 testuser1 testuser1 4096 Jul 26 10:06 . drwxr-xr-x. 3 root root 262144 Jul 26 10:03 .. -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:04 testfile1 -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:05 testfile2 drwxr-xr-x. 3 testuser1 testuser1 4096 Jul 26 10:07 testsubdir1 drwxr-xr-x. 2 testuser1 testuser1 4096 Jul 26 10:06 testsubdir2 [root@node1 testdir1]# mmgetacl testfile1 #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rw-c:allow (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (X)CHOWN (-)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED special:group@:r---:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (-)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED special:everyone@:r---:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (-)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
New files will now inherit the ACL from the parent directory, though the existing files have not been changed.
[root@node1 testdir1]# date > newfile1 [root@node1 testdir1]# ls -la newfile1 -rwx------. 1 root root 29 Jul 26 10:47 newfile1 [root@node1 testdir1]# mmgetacl newfile1 #NFSv4 ACL #owner:root #group:root special:owner@:rwxc:allow:Inherited (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED group:users:r-x-:allow:Inherited (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
In order to change the ACLs of the existing files, we will need to develop a script to recursively change each ACL to match the ACL for testdir1. There are any number of ways this could be done, but in this example, we will create two different files: one file containing the ACL for a directory, which includes the FileInherit and DirInherit flags, and one file containing the ACL for files, which does not have the inheritance flags.
We already have the directory file we need - it is the same as the ACL for testdir1: /tmp/testdir1.acl.txt. For clarity, we'll copy that to dir.acl.txt, and another copy to file.acl.txt, which we are going to edit to remove the inheritance flags.
[root@node1 testdir1]# cp /tmp/testdir1.acl.txt /tmp/dir.acl.txt [root@node1 testdir1]# cp /tmp/testdir1.acl.txt /tmp/file.acl.txt [root@node1 testdir1]# vi /tmp/file.acl.txt [root@node1 testdir1]# cat /tmp/dir.acl.txt #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow:FileInherit:DirInherit (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED group:users:r-x-:allow:FileInherit:DirInherit (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED [root@node1 testdir1]# cat /tmp/file.acl.txt #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED group:users:r-x-:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
Now, our shell script:
Newer version:
# cat setaclr-t1.sh #!/bin/bash # # For each file and subdirectory in the current directory, # set the ACL. For subdirectories, change to the subdirectory # and invoke this script recursively to change the ACL on # each of the files and subdirectories there. # 11/26/2024 Robert Leong # Added ./ in front of $LINE, this fixes - (dash) in file name like -CATALOG-", was breaking mmgetacl. # Added mmgetacl error check and exit to stop on error. # Added more verboseness for debug if needed. ls -1 | while read LINE do # f=$LINE f="./"$LINE # First get the acl from the current file. We want to keep the # owner and group unchanged, and those lines always start with the '#' echo "!!!!! working on path:" pwd echo "!!!!! file or directory:" echo $f echo "!!!!!" # sleep 5 if mmgetacl "$f"; then echo "mmgetacl OK" else echo "mmgetacl error" exit fi # sleep 5 mmgetacl "$f" | grep '^#' > /tmp/tmpacl.txt if [ -d "$f" ] then # Only use the lines containing the ACEs themselves. # Combine those ACEs with the existing owner and group info # we got above with the mmgetacl call cat /tmp/dir.acl.txt | grep -v '^#' >> /tmp/tmpacl.txt echo "Setting ACL on directory ${f}..." mmputacl "$f" -i /tmp/tmpacl.txt rm -f /tmp/tmpacl.txt pushd "$f" $0 popd else # For regular files cat /tmp/file.acl.txt | grep -v '^#' >> /tmp/tmpacl.txt echo "Setting ACL on file ${f}..." mmputacl "$f" -i /tmp/tmpacl.txt rm -f /tmp/tmpacl.txt fi done
Original version:
[root@node1 testdir1]# cat ~/setaclr.sh #!/bin/bash # # For each file and subdirectory in the current directory, # set the ACL. For subdirectories, change to the subdirectory # and invoke this script recursively to change the ACL on # each of the files and subdirectories there. ls -1 | while read LINE do f=$LINE # First get the acl from the current file. We want to keep the # owner and group unchanged, and those lines always start with the '#' mmgetacl "$f" | grep '^#' > /tmp/tmpacl.txt if [ -d "$f" ] then # Only use the lines containing the ACEs themselves. # Combine those ACEs with the existing owner and group info # we got above with the mmgetacl call cat /tmp/dir.acl.txt | grep -v '^#' >> /tmp/tmpacl.txt echo "Setting ACL on directory ${f}..." mmputacl "$f" -i /tmp/tmpacl.txt rm -f /tmp/tmpacl.txt pushd "$f" $0 popd else # For regular files cat /tmp/file.acl.txt | grep -v '^#' >> /tmp/tmpacl.txt echo "Setting ACL on file ${f}..." mmputacl "$f" -i /tmp/tmpacl.txt rm -f /tmp/tmpacl.txt fi done
A quick note about the 'ls -1 | while read LINE' construct: I often use something like 'for f in `ls`' when I am traversing a list of files in the current directory, but I realized that 'for ... in ...' won't work correctly for filenames that have spaces in them. Reading the entire line, as 'read' does, solves this particular problem. That got me thinking that there could also be issues with other non-alphanumeric characters in filenames (think '&' or '|'). I didn't test those types of scenarios for this exercise, but it may be something you need to guard against in a production environment.
Our original recursive directory listing:
[root@node1 testdir1]# ls -laR . .: total 259 drwx------. 4 testuser1 testuser1 4096 Jul 26 10:47 . drwxr-xr-x. 3 root root 262144 Jul 26 10:03 .. -rwx------. 1 root root 29 Jul 26 10:47 newfile1 -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:04 testfile1 -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:05 testfile2 drwxr-xr-x. 3 testuser1 testuser1 4096 Jul 26 10:07 testsubdir1 drwxr-xr-x. 2 testuser1 testuser1 4096 Jul 26 10:06 testsubdir2 ./testsubdir1: total 3 drwxr-xr-x. 3 testuser1 testuser1 4096 Jul 26 10:07 . drwx------. 4 testuser1 testuser1 4096 Jul 26 10:47 .. drwxr-xr-x. 2 testuser1 testuser1 4096 Jul 26 10:07 sub dir -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:07 test file 1 -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:07 test file 2 ./testsubdir1/sub dir: total 2 drwxr-xr-x. 2 testuser1 testuser1 4096 Jul 26 10:07 . drwxr-xr-x. 3 testuser1 testuser1 4096 Jul 26 10:07 .. -rw-r--r--. 1 testuser1 testuser1 29 Jul 26 10:07 test1 ./testsubdir2: total 1 drwxr-xr-x. 2 testuser1 testuser1 4096 Jul 26 10:06 . drwx------. 4 testuser1 testuser1 4096 Jul 26 10:47 ..
And after running the script:
[root@node1 testdir1]# ~/setaclr.sh Setting ACL on file newfile1... Setting ACL on file testfile1... Setting ACL on file testfile2... Setting ACL on directory testsubdir1... /ibm/gpfs1/testdir1/testsubdir1 /ibm/gpfs1/testdir1 Setting ACL on directory sub dir... /ibm/gpfs1/testdir1/testsubdir1/sub dir /ibm/gpfs1/testdir1/testsubdir1 Setting ACL on file test1... /ibm/gpfs1/testdir1/testsubdir1 Setting ACL on file test file 1... Setting ACL on file test file 2... /ibm/gpfs1/testdir1 Setting ACL on directory testsubdir2... /ibm/gpfs1/testdir1/testsubdir2 /ibm/gpfs1/testdir1 /ibm/gpfs1/testdir1 [root@node1 testdir1]# ls -laR . .: total 259 drwx------. 4 testuser1 testuser1 4096 Jul 26 10:47 . drwxr-xr-x. 3 root root 262144 Jul 26 10:03 .. -rwx------. 1 root root 29 Jul 26 10:47 newfile1 -rwx------. 1 testuser1 testuser1 29 Jul 26 10:04 testfile1 -rwx------. 1 testuser1 testuser1 29 Jul 26 10:05 testfile2 drwx------. 3 testuser1 testuser1 4096 Jul 26 10:07 testsubdir1 drwx------. 2 testuser1 testuser1 4096 Jul 26 10:06 testsubdir2 ./testsubdir1: total 3 drwx------. 3 testuser1 testuser1 4096 Jul 26 10:07 . drwx------. 4 testuser1 testuser1 4096 Jul 26 10:47 .. drwx------. 2 testuser1 testuser1 4096 Jul 26 10:07 sub dir -rwx------. 1 testuser1 testuser1 29 Jul 26 10:07 test file 1 -rwx------. 1 testuser1 testuser1 29 Jul 26 10:07 test file 2 ./testsubdir1/sub dir: total 2 drwx------. 2 testuser1 testuser1 4096 Jul 26 10:07 . drwx------. 3 testuser1 testuser1 4096 Jul 26 10:07 .. -rwx------. 1 testuser1 testuser1 29 Jul 26 10:07 test1 ./testsubdir2: total 1 drwx------. 2 testuser1 testuser1 4096 Jul 26 10:06 . drwx------. 4 testuser1 testuser1 4096 Jul 26 10:47 ..
You can see that the POSIX mode bits have been updated, while the file ownership has not been changed (newfile1 is still owned by root). The ACLs have been updated as well, for example:
[root@node1 testdir1]# mmgetacl testsubdir1 #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow:FileInherit:DirInherit (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED group:users:r-x-:allow:FileInherit:DirInherit (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED [root@node1 testdir1]# mmgetacl "testsubdir1/test file 1" #NFSv4 ACL #owner:testuser1 #group:testuser1 special:owner@:rwxc:allow (X)READ/LIST (X)WRITE/CREATE (X)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (X)DELETE_CHILD (X)CHOWN (X)EXEC/SEARCH (X)WRITE_ACL (X)WRITE_ATTR (X)WRITE_NAMED group:users:r-x-:allow (X)READ/LIST (-)WRITE/CREATE (-)APPEND/MKDIR (X)SYNCHRONIZE (X)READ_ACL (X)READ_ATTR (X)READ_NAMED (-)DELETE (-)DELETE_CHILD (-)CHOWN (X)EXEC/SEARCH (-)WRITE_ACL (-)WRITE_ATTR (-)WRITE_NAMED
By setting the FileInherit and DirInherit inheritance flags on an entry in the ACL, you can ensure that those permissions are inherited for newly created files. To set the ACLs recursively for existing files and directories, a simple script based on the example above can accomplish the task.
Additional resources:
Traditional GPFS ACL administration
https://www.ibm.com/docs/en/gpfs/4.1.0.4?topic=export-traditional-gpfs-acl-administration
0 Comments