How to Mirror File Permissions Between Identical Directory Structures in Linux/Unix


2 views

When maintaining multiple environments or doing data migrations, we often encounter identical directory structures where only the permissions need to be synchronized. This commonly occurs in:

  • Staging to production deployments
  • Backup restoration scenarios
  • Continuous integration setups

The most efficient method uses rsync's permission preservation features:

rsync -av --perms --dry-run /source/path/ /destination/path/

Remove --dry-run when ready to execute. The flags:

  • -a: Archive mode (recursive copy)
  • -v: Verbose output
  • --perms: Preserve permissions

For more control, combine find and chmod:

cd /source/path
find . -type d -exec chmod --reference={} /destination/path/{} \;
find . -type f -exec chmod --reference={} /destination/path/{} \;

When dealing with symbolic links or special permissions:

rsync -av --perms --links --specials --dry-run /source/path/ /destination/path/

To validate permission synchronization:

diff -r <(cd /source/path; find . -printf "%m %p\n" | sort) \
      <(cd /destination/path; find . -printf "%m %p\n" | sort)

For regular synchronization, create a bash script:

#!/bin/bash
SOURCE_DIR="/path/to/source"
DEST_DIR="/path/to/destination"

if [ ! -d "$SOURCE_DIR" ] || [ ! -d "$DEST_DIR" ]; then
    echo "Error: Directories don't exist" >&2
    exit 1
fi

rsync -av --perms --links --specials "$SOURCE_DIR"/ "$DEST_DIR"/

When working with mirrored directory structures in Linux/Unix systems, maintaining consistent permissions across different versions of the same file tree can be crucial for security and functionality. The scenario involves:

  • Source Tree: Directory structure with correct permissions (mode bits)
  • Target Tree: Identical directory structure (same paths/filenames) but with wrong permissions

The most efficient method combines find with chmod through command substitution:

find /path/to/source -type d -exec sh -c 'chmod --reference="{}" "/path/to/target/{}"' \;
find /path/to/source -type f -exec sh -c 'chmod --reference="{}" "/path/to/target/{}"' \;

For a robust solution that handles edge cases:

#!/bin/bash

SRC="/path/to/source"
DST="/path/to/target"

# Verify directory existence
if [ ! -d "$SRC" ] || [ ! -d "$DST" ]; then
    echo "Error: Source or target directory does not exist" >&2
    exit 1
fi

# Process directories first
find "$SRC" -type d | while read -r dir; do
    rel_path="${dir#$SRC}"
    target_dir="$DST$rel_path"
    if [ -d "$target_dir" ]; then
        chmod --reference="$dir" "$target_dir"
    fi
done

# Then process files
find "$SRC" -type f | while read -r file; do
    rel_path="${file#$SRC}"
    target_file="$DST$rel_path"
    if [ -f "$target_file" ]; then
        chmod --reference="$file" "$target_file"
    fi
done

Using rsync

For large directory trees, rsync can be more efficient:

rsync -a --no-owner --no-group --chmod=ugo=rwX /path/to/source/ /path/to/target/

Preserving ACLs

If extended attributes matter, use getfacl/setfacl:

getfacl -R /path/to/source > permissions.acl
setfacl --restore=permissions.acl
  • Always test with -exec echo first to verify paths
  • Consider using sudo for system directories
  • Check for symlinks with -L if needed
  • For SELinux contexts, add chcon --reference

For massive directory trees, this parallel approach can help:

find /path/to/source -print0 | parallel -0 -j8 chmod --reference={} /path/to/target/{}