Linux File Operations

File operations, permissions, and filesystem management.

Find Patterns

# BASIC FIND
find /path -name "*.txt"                 # By name (case sensitive)
find /path -iname "*.txt"                # Case insensitive
find /path -type f                       # Files only
find /path -type d                       # Directories only
find /path -type l                       # Symlinks only

# SIZE FILTERS
find /path -size +100M                   # Larger than 100MB
find /path -size -1k                     # Smaller than 1KB
find /path -size 50M                     # Exactly 50MB
find /path -empty                        # Empty files/dirs

# TIME FILTERS
find /path -mtime -1                     # Modified in last 24h
find /path -mtime +7                     # Modified more than 7 days ago
find /path -mmin -30                     # Modified in last 30 minutes
find /path -newer reference_file         # Newer than reference

# TIME FILTER EXPLAINED:
# -mtime n  : Modified n*24 hours ago
# -mtime -n : Modified less than n*24 hours ago
# -mtime +n : Modified more than n*24 hours ago
# Same pattern for -atime (access), -ctime (change)

# PERMISSION FILTERS
find /path -perm 644                     # Exact permissions
find /path -perm -644                    # At least these permissions
find /path -perm /u+x                    # User executable (any match)
find /path -perm -u+w,g+w                # User AND group writable

# OWNER FILTERS
find /path -user evanusmodestus          # By user
find /path -group wheel                   # By group
find /path -nouser                        # No user (orphaned)
find /path -nogroup                       # No group

# COMBINING FILTERS
find /path -type f -name "*.log" -size +10M -mtime +7
find /path \( -name "*.txt" -o -name "*.md" \)  # OR logic
find /path -type f ! -name "*.bak"       # NOT logic

# EXEC ACTIONS
find /path -type f -name "*.bak" -delete  # Delete (careful!)
find /path -type f -exec chmod 644 {} \;  # Change permissions
find /path -type f -exec chmod 644 {} +   # Batch mode (faster)

# EXEC vs + EXPLAINED:
# {} \;  - Runs command once per file: chmod 644 file1; chmod 644 file2
# {} +   - Batches files: chmod 644 file1 file2 file3 (much faster)

# XARGS PATTERNS (even faster)
find /path -type f -name "*.log" -print0 | xargs -0 rm -f
find /path -type f -name "*.txt" | xargs grep "pattern"

# -print0 and xargs -0 handle filenames with spaces/special chars

# ADVANCED: Find + AWK
# Large files with sizes
find /var -type f -size +50M -exec ls -lh {} \; 2>/dev/null | \
    awk '{print $5, $9}' | sort -hr

# Files modified today by hour
find /var/log -type f -mtime 0 -ls 2>/dev/null | \
    awk '{print $9}' | cut -d: -f1 | sort | uniq -c

# PRUNE DIRECTORIES (don't descend)
find / -path /proc -prune -o -name "*.conf" -print
find / \( -path /proc -o -path /sys \) -prune -o -type f -name "*.log" -print

# INFRASTRUCTURE: Find configs across systems
for host in vault-01 bind-01 ise-01; do
    echo "=== $host ==="
    ssh "$host" "find /etc -name '*.conf' -mtime -1 2>/dev/null" | head -5
done

Permissions

# PERMISSION BASICS
# rwx = 4+2+1 = 7
# r-x = 4+0+1 = 5
# r-- = 4+0+0 = 4

# CHMOD - Change Mode
chmod 755 file                           # rwxr-xr-x
chmod 644 file                           # rw-r--r--
chmod 600 file                           # rw-------

# Symbolic mode
chmod u+x file                           # Add user execute
chmod g-w file                           # Remove group write
chmod o= file                            # Remove all other permissions
chmod a+r file                           # Add read for all
chmod u=rwx,g=rx,o= file                 # Explicit

# RECURSIVE
chmod -R 755 dir                         # All files and dirs
chmod -R u+rwX,go+rX dir                 # Capital X = execute only on dirs

# X (capital) EXPLAINED:
# x = always add execute
# X = add execute only if directory OR already has execute
# Perfect for mixed file/dir recursion

# SPECIAL PERMISSIONS
# setuid (4xxx) - Execute as file owner
chmod u+s /path/to/binary                # Or chmod 4755
# Only useful for binaries, dangerous if misused

# setgid (2xxx) - Execute as file group / inherit group for dirs
chmod g+s /path/to/dir                   # Or chmod 2755
# New files inherit directory's group - great for shared folders

# sticky bit (1xxx) - Only owner can delete files
chmod +t /tmp                            # Or chmod 1777
# Standard for /tmp - anyone can create, only owner can delete

# CHOWN - Change Owner
chown user file                          # Change owner
chown user:group file                    # Change owner and group
chown :group file                        # Change group only
chown -R user:group dir                  # Recursive

# UMASK - Default Permissions
umask                                    # Show current umask
umask 022                                # New files: 644, dirs: 755
umask 077                                # New files: 600, dirs: 700

# HOW UMASK WORKS:
# Files: 666 - umask = permissions
# Dirs:  777 - umask = permissions
# umask 022: files=644, dirs=755
# umask 077: files=600, dirs=700

# SECURITY PATTERNS
# SSH keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_*
chmod 644 ~/.ssh/*.pub
chmod 600 ~/.ssh/config

# Web directories
chown -R www-data:www-data /var/www
chmod -R u=rwX,g=rX,o= /var/www

# Secrets
chmod 600 /etc/ssl/private/*.key
chmod 644 /etc/ssl/certs/*.crt

# FIND PERMISSION ISSUES
# World-writable files (security risk)
find /etc -type f -perm /o+w 2>/dev/null

# SUID binaries (potential privilege escalation)
find / -perm -4000 -type f 2>/dev/null

# Files without owner
find / -nouser -o -nogroup 2>/dev/null

Access Control Lists (ACLs)

# ACLs provide fine-grained permissions beyond user/group/other
# Filesystem must be mounted with acl option (default on ext4)

# CHECK ACL SUPPORT
mount | grep acl                         # Check mount options
getfacl /path/to/file                   # View ACLs

# VIEW ACLs
getfacl file                            # Full ACL
getfacl -R dir                          # Recursive
ls -l file                              # + at end means ACL exists
# -rw-r--r--+ means ACLs are set

# SET ACLs
setfacl -m u:evanusmodestus:rwx file    # Add user permission
setfacl -m g:devops:rx file             # Add group permission
setfacl -m o::r file                    # Modify other

# MULTIPLE ACLs AT ONCE
setfacl -m u:user1:rwx,u:user2:rx,g:team:rwx file

# REMOVE ACLs
setfacl -x u:username file              # Remove specific user ACL
setfacl -b file                         # Remove all ACLs

# DEFAULT ACLs (for directories - new files inherit)
setfacl -d -m u:evanusmodestus:rwx dir  # Default for new files
setfacl -d -m g:team:rx dir             # Group default

# VIEW DEFAULT ACLs
getfacl dir                             # Shows default: entries

# RECURSIVE
setfacl -R -m u:backup:rx /var/log      # Apply to all files
setfacl -R -d -m u:backup:rx /var/log   # Set defaults recursively

# COPY ACLs
getfacl file1 | setfacl --set-file=- file2

# BACKUP AND RESTORE ACLs
getfacl -R /path/to/dir > acls.txt
setfacl --restore=acls.txt

# ACL MASK
# The mask limits maximum effective permissions for named users/groups
setfacl -m m::rx file                   # Set mask to r-x

# EFFECTIVE PERMISSIONS
# getfacl shows "effective" when mask limits permissions:
# user:dev:rwx    #effective:r-x

# INFRASTRUCTURE: Shared backup directory
# Allow backup user to read all, team to read/write
setfacl -R -m u:backup:rx /backup
setfacl -R -m g:ops:rwx /backup
setfacl -R -d -m u:backup:rx /backup
setfacl -R -d -m g:ops:rwx /backup

# VERIFY
getfacl /backup | grep -E '^(user|group|default):'

File Attributes (chattr/lsattr)

# Extended attributes beyond standard permissions
# Common on ext4, may vary on other filesystems

# VIEW ATTRIBUTES
lsattr file                             # Single file
lsattr -R dir                           # Recursive
lsattr -d dir                           # Directory itself

# ATTRIBUTE FLAGS:
# i = immutable (can't modify, delete, rename, or link)
# a = append only (can only append, not modify)
# s = secure delete (zeros on delete)
# u = undeletable (recoverable after delete)
# c = compressed
# A = no atime updates
# S = synchronous updates

# SET ATTRIBUTES
chattr +i file                          # Make immutable
chattr -i file                          # Remove immutable
chattr +a /var/log/audit.log            # Append only
chattr +A file                          # Don't update atime

# IMMUTABLE FILE (even root can't modify!)
chattr +i /etc/resolv.conf              # Prevent DNS changes
# To modify: chattr -i first

# APPEND ONLY (great for logs)
chattr +a /var/log/secure               # Can only append
# Prevents log tampering (must remove attr to truncate)

# SECURITY PATTERN: Protect critical configs
chattr +i /etc/passwd
chattr +i /etc/shadow
chattr +i /etc/sudoers

# WARNING: You must remove immutable before updates!
# System updates will fail if package configs are immutable

# CHECK BEFORE UPDATING
lsattr /etc/passwd /etc/shadow 2>/dev/null | grep -E '^....i'

# INFRASTRUCTURE: Find immutable files
find /etc -type f -exec lsattr {} \; 2>/dev/null | grep -E '^....i'

# EXTENDED ATTRIBUTES (xattr)
# Key-value pairs attached to files (used by SELinux, capabilities, etc.)
getfattr -d file                        # List xattrs
getfattr -n user.myattr file            # Get specific
setfattr -n user.myattr -v "value" file # Set
setfattr -x user.myattr file            # Remove

# SELinux context (on RHEL/Rocky)
ls -Z file                              # View SELinux context
getfattr -n security.selinux file       # Raw xattr
# SYMBOLIC (SOFT) LINKS
ln -s /path/to/target /path/to/link     # Create symlink
ln -sf /new/target /path/to/link        # Force (replace existing)

# Symlink properties:
# - Points to a path (can be relative or absolute)
# - Can point to files or directories
# - Can cross filesystems
# - Breaks if target is deleted/moved

# HARD LINKS
ln /path/to/target /path/to/link        # Create hard link

# Hard link properties:
# - Points to inode (same data blocks)
# - Only for files (not directories)
# - Must be on same filesystem
# - Target can be deleted, link still works
# - Both names are "equal" - no original

# VIEW LINK INFO
ls -l link                              # Shows -> target for symlinks
ls -li file                             # Shows inode number
stat file                               # Detailed info including links

# FIND HARD LINKS TO A FILE
find / -samefile /path/to/file 2>/dev/null
# Or by inode:
find / -inum 12345 2>/dev/null

# FIND BROKEN SYMLINKS
find /path -xtype l                     # Broken symlinks
find /path -type l ! -exec test -e {} \; -print  # Alternative

# FIX BROKEN SYMLINKS
# Find and show targets
find /usr/local/bin -type l -exec ls -l {} \; | grep -E 'broken|No such'

# INFRASTRUCTURE: Dotfiles with stow
cd ~/dotfiles
stow -v vim                             # Symlink vim configs
stow -v -D vim                          # Remove symlinks
stow -v -R vim                          # Restow (fix)

# COMMON SYMLINK PATTERNS
# Executable in PATH
ln -s ~/bin/myscript.sh ~/.local/bin/myscript

# Version switching
ln -sf /opt/java-17 /opt/java           # Point to active version
ln -sf python3.11 /usr/bin/python

# Config management
ln -s ~/.dotfiles/vimrc ~/.vimrc
ln -s ~/.dotfiles/zshrc ~/.zshrc

# READLINK - resolve symlinks
readlink link                           # Direct target
readlink -f link                        # Full canonical path
realpath link                           # Full canonical path (alternative)

# RELATIVE SYMLINKS
ln -sr target link                      # -r makes relative
# Useful for portable directory structures

File Integrity

# CHECKSUMS
md5sum file                             # MD5 (legacy, not secure)
sha256sum file                          # SHA-256 (preferred)
sha512sum file                          # SHA-512

# Generate checksums for multiple files
sha256sum *.tar.gz > SHA256SUMS
sha256sum -c SHA256SUMS                 # Verify all

# VERIFY DOWNLOAD
curl -LO https://example.com/file.tar.gz
curl -LO https://example.com/file.tar.gz.sha256
sha256sum -c file.tar.gz.sha256

# FILE METADATA
stat file                               # Full metadata
stat -c '%n %s %Y' file                 # Custom format
# %n=name, %s=size, %Y=mtime (epoch)

stat -c '%a %U:%G %n' *                 # Permissions owner:group name

# COMPARE FILES
diff file1 file2                        # Text diff
diff -u file1 file2                     # Unified format
diff -r dir1 dir2                       # Recursive directory diff
cmp file1 file2                         # Byte-by-byte (binary safe)

# CONFIG DRIFT DETECTION
# Baseline
sha256sum /etc/ssh/sshd_config > ~/baselines/sshd_config.sha256

# Check for changes
sha256sum -c ~/baselines/sshd_config.sha256
# sshd_config: OK   or   sshd_config: FAILED

# INFRASTRUCTURE: Config drift across hosts
baseline_hash="abc123..."
for host in vault-01 bind-01 ise-01; do
    hash=$(ssh "$host" "sha256sum /etc/ssh/sshd_config 2>/dev/null" | awk '{print $1}')
    if [[ "$hash" == "$baseline_hash" ]]; then
        echo "$host: OK"
    else
        echo "$host: DRIFTED ($hash)"
    fi
done

# AIDE (Advanced Intrusion Detection Environment)
# Install: pacman -S aide / apt install aide
aide --init                             # Create initial database
aide --check                            # Check for changes
aide --update                           # Update after changes

# /etc/aide.conf
# /etc    CONTENT_EX                    # Monitor /etc
# /bin    BIN_EX                        # Monitor binaries
# !/var/log                             # Exclude logs

# TRIPWIRE (alternative to AIDE)
tripwire --init                         # Initialize
tripwire --check                        # Check integrity

# RSYNC DRY-RUN FOR COMPARISON
rsync -avnc /etc/ backup:/etc/          # -n = dry-run, shows differences
# c = checksum (slower but accurate)

File Monitoring (inotify)

# INOTIFYWAIT - monitor file/directory events
# Install: pacman -S inotify-tools / apt install inotify-tools

# Watch single file
inotifywait -m /etc/passwd              # Monitor continuously

# Watch directory
inotifywait -m /var/log                 # Changes in directory
inotifywait -mr /var/log                # Recursive

# EVENTS:
# access    - File read
# modify    - File modified
# attrib    - Attributes changed
# close     - File closed
# open      - File opened
# create    - File created
# delete    - File deleted
# move      - File moved

# Filter specific events
inotifywait -m -e modify,create,delete /etc

# OUTPUT FORMATS
inotifywait -m --format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S' /etc

# PRACTICAL: Watch config changes and log
inotifywait -mr -e modify,create,delete --format '%T %w%f %e' \
    --timefmt '%Y-%m-%d %H:%M:%S' /etc 2>/dev/null | \
    tee -a /var/log/config-changes.log

# TRIGGER ACTION ON CHANGE
inotifywait -m -e modify /etc/nginx/nginx.conf | while read path action file; do
    echo "Config changed, reloading nginx..."
    systemctl reload nginx
done

# AUTO-RELOAD ON SAVE (development)
inotifywait -mr -e close_write --format '%w%f' /path/to/src | while read file; do
    echo "File changed: $file"
    make build  # or whatever build command
done

# ENTR - simpler alternative for development
# Install: pacman -S entr / apt install entr
ls *.py | entr python test.py           # Run tests on change
find . -name '*.go' | entr go build     # Rebuild on change

# WATCH COMMAND (poll-based, simpler)
watch -n 1 'ls -la /tmp'                # Every 1 second
watch -d 'df -h'                        # Highlight changes
watch -d -n 5 'kubectl get pods'        # k8s pod status

# INFRASTRUCTURE: Monitor critical configs
inotifywait -m -e modify \
    /etc/ssh/sshd_config \
    /etc/pam.d/sshd \
    /etc/sudoers | while read path action file; do
    echo "[$(date)] ALERT: $path was modified"
    # Could send to Wazuh/Slack here
done

Disk Usage Analysis

# BASIC USAGE
df -h                                    # Filesystem usage
df -i                                    # Inode usage
du -sh /path                            # Directory size

# SORTED BY SIZE
du -h --max-depth=1 /var | sort -hr     # One level, sorted
du -ah /var | sort -hr | head -20       # All files, top 20

# STAY ON FILESYSTEM
du -xh --max-depth=2 / 2>/dev/null | sort -hr | head -30
# -x prevents crossing mount points

# NCDU - Interactive exploration
ncdu /var                               # Navigate with arrows
ncdu -x /                               # Stay on filesystem
ncdu -e /                               # Show hidden files

# FIND LARGE FILES
find / -type f -size +100M -exec ls -lh {} \; 2>/dev/null | sort -k5 -hr | head -20

# With awk formatting
find / -type f -size +100M -ls 2>/dev/null | \
    awk '{size=$7; name=$11; printf "%10.2f MB  %s\n", size/1024/1024, name}' | \
    sort -rn | head -20

# FIND OLD LARGE FILES
find /var/log -type f -size +50M -mtime +30 -ls 2>/dev/null

# ANALYZE LOG ROTATION
ls -lhS /var/log/*.log                  # Sorted by size
ls -lt /var/log/*.log | head -10        # Sorted by time

# SPACE BY FILE TYPE
find /path -type f -name "*.log" -exec du -ch {} + | tail -1
find /path -type f -name "*.tar.gz" -exec du -ch {} + | tail -1

# JOURNAL USAGE (systemd)
journalctl --disk-usage                  # Show journal size
journalctl --vacuum-size=500M            # Reduce to 500MB
journalctl --vacuum-time=7d              # Keep only 7 days

# DOCKER/CONTAINER CLEANUP
docker system df                         # Docker disk usage
docker system prune -a                   # Clean unused

# INFRASTRUCTURE: Multi-host analysis
for host in kvm-01 vault-01 bind-01 ise-01; do
    echo "=== $host ==="
    ssh "$host" "df -h / /var 2>/dev/null | tail -n +2" 2>/dev/null | \
        awk -v h="$host" '{printf "  %s: %s used of %s (%s)\n", $6, $3, $2, $5}'
done

# Find what's filling up
ssh kvm-01 "du -xh --max-depth=2 /var 2>/dev/null | sort -hr | head -10"

# CLEAN PACKAGE CACHE
# Arch
paccache -rk 2                          # Keep last 2 versions
pacman -Sc                              # Clean uninstalled packages

# RHEL/Rocky
dnf clean all
dnf autoremove

# Debian/Ubuntu
apt clean
apt autoremove

File Gotchas

# WRONG: find -exec with filenames containing spaces
find /path -name "*.txt" -exec cat {} \;
# May break on spaces

# CORRECT: Use -print0 and xargs -0
find /path -name "*.txt" -print0 | xargs -0 cat

# WRONG: rm -rf with variable that might be empty
rm -rf $DIR/                            # If DIR is empty, this becomes rm -rf /

# CORRECT: Quote and verify
[[ -n "$DIR" ]] && rm -rf "$DIR/"
# Or use parameter expansion
rm -rf "${DIR:?Variable is empty!}/"

# WRONG: Relying on ls output in scripts
for f in $(ls *.txt); do                # Breaks on spaces
    echo "$f"
done

# CORRECT: Use glob directly
for f in *.txt; do
    [[ -e "$f" ]] || continue           # Handle no matches
    echo "$f"
done

# WRONG: chmod -R 755 on everything
chmod -R 755 /var/www                   # Files become executable!

# CORRECT: Different permissions for files/dirs
find /var/www -type d -exec chmod 755 {} \;
find /var/www -type f -exec chmod 644 {} \;
# Or use X
chmod -R u=rwX,g=rX,o=rX /var/www

# WRONG: Removing immutable file
rm -f /etc/resolv.conf                  # Operation not permitted

# CORRECT: Remove immutable first
chattr -i /etc/resolv.conf
rm -f /etc/resolv.conf

# WRONG: Symlink to relative path from wrong directory
cd /opt
ln -s myapp/bin/run /usr/local/bin/run  # Broken! Points to /usr/local/bin/myapp/...

# CORRECT: Use absolute path or -r
ln -s /opt/myapp/bin/run /usr/local/bin/run
# Or relative from link location
ln -sr /opt/myapp/bin/run /usr/local/bin/run

# WRONG: ACL not preserved in cp
cp file1 file2                          # ACLs lost

# CORRECT: Preserve with -a or --preserve=all
cp -a file1 file2
cp --preserve=all file1 file2

# WRONG: Checking file exists with ls
if ls file 2>/dev/null; then            # Wrong exit code check

# CORRECT: Use test/[[ ]]
[[ -f file ]] && echo "exists"
[[ -d dir ]] && echo "is directory"
[[ -L link ]] && echo "is symlink"

Quick Reference

# FIND
find /path -name "*.log" -mtime -1      # Modified today
find /path -size +100M                  # Large files
find /path -type f -exec chmod 644 {} + # Batch chmod

# PERMISSIONS
chmod 755 dir; chmod 644 file           # Standard
chmod -R u=rwX,g=rX,o= /path            # Recursive safe
chown -R user:group /path               # Change owner

# ACLs
getfacl file                            # View
setfacl -m u:user:rwx file              # Set
setfacl -b file                         # Remove all

# ATTRIBUTES
lsattr file                             # View
chattr +i file                          # Immutable
chattr +a file                          # Append only

# LINKS
ln -s target link                       # Symlink
ln target link                          # Hard link
readlink -f link                        # Resolve

# INTEGRITY
sha256sum file > file.sha256            # Generate
sha256sum -c file.sha256                # Verify

# MONITORING
inotifywait -mr -e modify /path         # Watch changes
watch -d -n 5 'command'                 # Poll changes

# DISK USAGE
du -xh --max-depth=2 / | sort -hr       # Space usage
ncdu /var                               # Interactive
find / -type f -size +100M              # Large files