NFSv4 ACLs in a GPFS filesystem and How to set these recursively

Faizal Iqbal -

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:

  1. Remove access for "special:group" and "everyone" - the equivalent of a 700 or 600 POSIX permission.
  2. Add read-only access for the group "users"
  3. 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

 

Have more questions? Submit a request

0 Comments

Please sign in to leave a comment.