Monday, December 22, 2025

Backup Permissions: Full Disk Access to backup script ran by LaunchAgent (com.user.backup.plist)?

cp -R {target} {dest} gives ‘Operation not permitted’ error during background backup script execution

See the code from com.user.backup.plist and backup-script.sh below.

I tried to go to System Settings → Privacy & Security → Full Disk Access to grant access to the backup script ~/backup-script.sh which is an executable (-rwxr-xr-x@) that is executed by ~/Library/LaunchAgents/com.user.backup.plist every Saturday at 03:00 h but after using ⌘⇧. to show all files, I still cannot select it. My next idea would be to try to grant launchd itself full disk access, but this seems like a Bad Idea™ security-wise.

Besides helping with my system here, which is good when ran from the command line –

What are people doing to backup particular directories besides just using Time Machine?

sw_vers

% :: sw_vers                                                                                                                       13:15:42 (130)
ProductName:        macOS
ProductVersion:     15.6.1
BuildVersion:       24G90

~/Library/LaunchAgents/com.user.backup.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.backup</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/user/backup-script.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>3</integer>
        <key>Minute</key>
        <integer>0</integer>
        <key>Weekday</key>
        <integer>6</integer>
    </dict>
    <key>StandardErrorPath</key>
    <string>/tmp/backup.err</string>
    <key>StandardOutPath</key>
    <string>/tmp/backup.out</string>
    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>

~/backup-script.sh

#!/bin/bash

### ----------------
### Disk Capacities
### ----------------
### A management task should be to prune these directories, and then test the
### /Users/user/cleanup-script.sh --dry-run (using the dry-run flag) and tail -f to follow logfiles

### ----------------
### Configuration
### ----------------
# Define directories and/or files to back up
# _Exclude_ trailing / from directories for cp -R's expected behavior!
BACKUP_TARGETS=(
    "target1"
    "target2"
)

### ----------------
### Configuration
### ----------------
# Define external volumes
# Intentionally withheld final "https://apple.stackexchange.com/"
VOLUMES=(
    "/Volumes/External/Backups"
    "/Volumes/Samsung USB/backups"
)

### ----------------
### Configuration
### ----------------
### Logging
### ----------------
# Set the logfile directory
# If on startup this directory doesn't exist, the script will quit
# and instruct the administrator to specify the LOGFILE_DIR
# and ensure that other configuration variables like BACKUP_DIRS
# are properly set 
LOGFILE_DIR="/Users/user/backup-cleanup-logs"

if [ ! -d "$LOGFILE_DIR" ]; then
    error_LOGFILE_DIR_not_exists=$(cat <<EOF
Error: \$LOGFILE_DIR is set to $LOGFILE_DIR that does not exist.

Review configuration of $0, setting \$BACKUP_DIRS and \$LOGFILE_DIR
EOF
    )

    echo "$error_LOGFILE_DIR_not_exists"
    exit 1
fi

# Define the date format for log files and backup naming
DATE=$(date +%Y-%m-%d)

# Define log file
LOGFILE="$LOGFILE_DIR/backup-log-$DATE.log"

# Function to perform backup
# Rename any hidden files prepended by '.' to "dot_" followed by their proper name
backup() {
    for TARGET in "${BACKUP_TARGETS[@]}"; do
        for VOLUME in "${VOLUMES[@]}"; do
            BACKUP_NAME="$(basename "$TARGET"|sed 's/\./dot_/')-$DATE"
            BACKUP_PATH="$VOLUME/$BACKUP_NAME"
            if cp -R "$TARGET" "$BACKUP_PATH"; then
                echo "Backup of $DIR to $BACKUP_PATH successful." >> "$LOGFILE"
            else
                echo "Error backing up $DIR to $BACKUP_PATH." >> "$LOGFILE"
            fi
        done
    done
}

# Run backup function
backup

echo "Finished backing up..."

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles