0

We're been running tar/gzip backups for a while on Fedora 25 Linux servers and we'd like to make sure aquota.user files get backed up. From the comments in the drobo-backup.conf file here's what we have:

# Because aquota.user cannot have its atime reset, tar will give
# an error status=2 if --atime-preserve is used when backing it up.
# We don't want to lose the backup of this file but we also don't
# want to routinely ignore tar status=2.  Solution is to make
# --atime-preserve a per-backup argument and omit it when backing
# up aquota.user separately.

However when running via cron the email notification comes back with:

/bin/tar: home/users/aquota.user: Cannot utime: Operation not permitted
/bin/tar: Exiting with failure status due to previous errors

Backup of /home/users FAILED

Failed to rename /home-users-new.tgz to
/home-users-FAILED.tgz: File exists
Backed up /home/users/aquota.user to
/home-users-aquota.user.tgz

gzip: stdout: Input/output error
/bin/tar: Child returned status 1
/bin/tar: Error is not recoverable: exiting now

Here are the options in the config file:

tarargs= --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed
backup = /home/users --atime-preserve --exclude=aquota.user --exclude=.gvfs --exclude=--exclude-ignore-recursive=S.gpg-agent --exclude=.adobe --exclude=.config/libvirt/qemu/lib/capabilities.monitor.sock --exclude=.dropbox --exclude=home/users/*/.cache --exclude=.fontconfig --exclude=csga/*/S.gpg-agent --exclude-caches-all
backup = /home/users/aquota.user
backup = /home --atime-preserve --exclude=.gvfs --exclude=.gnupg
backup = /etc --atime-preserve
backup = /root --atime-preserve --exclude=.cache --exclude=.cache/keyring-* --exclude-caches-all
backup = /usr/local --atime-preserve
backup    = /var/lib --atime-preserve --exclude=var/lib/rpm --exclude=var/lib/yum --exclude=mysql/mysql.sock --exclude=gssproxy/default.sock --exclude=samba/private/msg.sock
backup    = /var/backup --atime-preserve
backup    = /var/log --atime-preserve
backup    = /var/spool --atime-preserve
backup    = /var/www --atime-preserve

Here is the full backup script:

use POSIX;
# Global variables
# Host name will be used as name of directory for backups on drobo
my($hostname)=`/bin/hostname`;
chomp($hostname);
my($configfile)="/etc/drobo-backup.conf";
my($tar)="/bin/tar";        # Path to tar utility
my($mkdir)="/bin/mkdir";    # Path to mkdir utility
my($verbose)=0;
my($testmode)=0;

sub Usage {
    print "Usage: drobo-backup [-v] [-c configfile]\n";
    print "   -v : verbose mode";
    print "   -c : specify configuration file (default $configfile)";
    print "   -n : printd but don't execute commands (for testing config)";
    exit 1;
}

# Subroutine to back up one directory to drobo
sub do_backup {
    my($drobo,$args,$backup,$cond) = @_;

# The backup arg may include per-backup tar args.  Strip off these
# and quotes to get at the filename
    my($backuppath)=$backup;
# If quoted, remove quotes for naming.  Otherwise it is required not
# not to have embedded blanks so that per-backup tar arguments may follow.
    if( $backuppath =~ /^"([^"]*)"/ ) { # double quotes
    $backuppath = $1;
    }
    elsif( $backuppath =~ /^'([^']*)'/ ) { # single quotes
    $backuppath = $1;
    }
    elsif( $backuppath =~ /^(\S*)/ ) { # otherwise no blanks in path
    $backuppath = $1;
    }

    if( -d $backuppath || -f $backuppath ) { # check it is a valid dir or file
    my($drobodir) = "$drobo/$hostname";
    # make sure the drobo subdirectory for this host exists
    my($mkdircmd) = "$mkdir $drobodir";
    if( $verbose ) {
        print "$mkdircmd\n";
    }
    if( ! ($testmode || -d $drobodir) ) {
        system($mkdircmd);
    }
    # if it did not work, bail.
    if( ! ($testmode || -d $drobodir) ) {
        print "Failed to create destination dir $drobodir\n";
        exit 1;
    }
# construct tarfile name, e.g. usr-local.tgz
    my($backupfilestem) = $backuppath;
                # Sanitize the tarfile name
    while($backupfilestem =~ s,^/,,) { next; } # remove any leading slash(es)
    while($backupfilestem =~ s,/$,,) { next; } # remove any trailing slash(es)
    $backupfilestem =~ s,/,-,g; # all internal slashes become hyphens
    $backupfilestem =~ s/[^-\.\w]/X/g;# all remaining non-word chars exc . become X

    my($backupname)="$drobodir/$backupfilestem-new.tgz";
    my($backuprename) = "$drobodir/$backupfilestem.tgz";
#   $backup =~ s|/|| if $backup =~ m|^['"]?/|; # remove any leading slash
#   my($tarcmd) = "$tar -C / -czf $backupname $args $backup";

    my($tarcmd);
    if($backup =~ m|^['"]?/|){
       $backup =~ s|/||;  # remove any leading slash
       $tarcmd = "ionice -c2 -n7 nice -n19 $tar -czf $backupname -C / $args $backup";
    }else{
       $tarcmd = "ionice -c2 -n7 nice -n19 $tar -czf $backupname $args $backup";
    }

    if( $cond ) {
        $cond =~ s/^\[//;   # remove the [ ] around condition
        $cond =~ s/\]$//;
        $cond =~ s/BKPATH/$backuppath/g; # convenience substitutions
        $cond =~ s/TARFILE/$backuprename/g;
        $cond =~ s/TARDIR/$drobodir/g;
    }
    if( $cond && WEXITSTATUS(system("test $cond")) != 0 ) {
        if( $verbose ) {
        print "Condition [$cond] tests false\n";
        print "No backup of $backuppath\n";
        }
    }
    else {
        if( $verbose ) {
        if( $cond ) {
            print "Condition [$cond] tests true\n";
        }
        print "$tarcmd\n";
        }
    # tar returns 0 for success, 1 for warnings such as file changed while
    # being copied.  So we take either as meaning success.  Rename foo-new.tgz
    # to the (usually existing) foo.tgz.  N.B. system() returns status<<8.
        if( !$testmode ) {
        if( WEXITSTATUS(system($tarcmd)) >= 2 ) {
            print "\nBackup of $backuppath FAILED\n\n";
            # to avoid bad backup being renamed to good in second try, call it bad
            $backuprename = "$drobodir/$backupfilestem-FAILED.tgz";
        }
        if( rename("$backupname","$backuprename") ) {
            print "Backed up $backuppath to $backuprename\n";
        }
        else {
            print "Failed to rename $backupname to $backuprename: $!\n";
        }
        }
    }
    }
    else {
    print "$backuppath is not a directory or file\n";
    }
}



# default arguments to use on every backup
my($tarargs)="--atime-preserve --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed";


# set default drobopath according network

my($drobopath)="/domain/subdomain"; # cis value
if($hostname =~ /\.our\.domain\.edu/) {
    $drobopath="/domain/subdomain"; # dsm value
}


# Process command line arguments
while(@ARGV) {
    if( $ARGV[0] eq "-c" ) {    # -c configfile
    shift (@ARGV);
    if(@ARGV) {
        $configfile = $ARGV[0];
    }
    else {
        Usage();
    }
    }
    elsif( $ARGV[0] eq "-v" ) { # -v (verbose mode)
    ++$verbose;
    }
    elsif( $ARGV[0] eq "-n" ) { # -n (no-exec mode)
    $testmode = 1;
    }
    else {          # unrecognized argument
    Usage();
    }

    shift (@ARGV);
}


open(CONFIGFILE,$configfile) || die("Cannot open configfile $configfile: $!");

if($verbose) {
    print "Reading configfile $configfile\n";
}

my(@backup);
my(%condition);
my($configline) = 0;
foreach (<CONFIGFILE>) {
    $configline++;
    if( /^\s*#/ || /^\s*$/ ) {  # skip blank & comment lines (first nonblank is #)
    next;
    }
                # drobo=/path/to/drobo
    if( /^\s*drobo\s*=\s*(.*)$/ ) {
    $drobopath=$1;
    }
                # tarargs=global tar arguments
    elsif( /^\s*tarargs\s*=\s*(.*)$/ ) {
    $tarargs=$1;
    }
                # backup [condition] =/path/for/backup [tar args]
    elsif( /^\s*backup\s*(\[[^\]]*\])?\s*=\s*(.*)$/ ) {
    push(@backup,$2);
    if( $1 ) {
        $condition{$2} = $1;
    }
    }
    else {
    print "Unknown config directive at line $configline in $configfile:\n";
    print;
    exit 1;
    }
}

close(CONFIGFILE);


my($path);
foreach $path (@backup) {
    do_backup($drobopath,$tarargs,$path,$condition{$path});
}


# For unknown reason, rename of some files often fails.  Try again here.

foreach $tarfile ( glob("$drobopath/$hostname/*-new.tgz") ) {
    $rename_name = $tarfile;
    $rename_name =~ s/-new\.tgz$/.tgz/;
    if( rename($tarfile,$rename_name) ) {
    print "Second try renamed $tarfile to $rename_name\n";
    }
}
1

1 Answer 1

0

Here is the output of the command in test mode. Not sure why this would fail with the --exclude=aquota.user in the first tar command.

/usr/local/sbin/drobo-backup -c /etc/drobo-backup.conf -v -n
Reading configfile /etc/drobo-backup.conf
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/home-users-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz home/users --atime-preserve --exclude=aquota.user --exclude=.gvfs --exclude=S.gpg-agent --exclude=.adobe  --exclude=.dropbox --exclude=.cache --exclude-caches-all 
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/home-users-aquota.user-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz home/users/aquota.user
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/home-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz home --atime-preserve --exclude=.gvfs --exclude=.gnupg  --exclude=aquota.user
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/etc-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz etc --atime-preserve
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/root-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz root --atime-preserve --exclude=.cache --exclude=.cache/keyring-* --exclude-caches-all
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/usr-local-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz usr/local --atime-preserve
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/var-lib-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz var/lib --atime-preserve --exclude=var/lib/rpm --exclude=var/lib/yum --exclude=mysql/mysql.sock --exclude=gssproxy/default.sock --exclude=samba/private/msg.sock
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/var-backup-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz var/backup --atime-preserve
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/var-log-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz var/log --atime-preserve
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/var-spool-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz var/spool --atime-preserve
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/var-www-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz var/www --atime-preserve
/bin/mkdir /ourdomain
ionice -c2 -n7 nice -n10 /bin/tar -cf /ourdomain/var-yp-new.tgz -C / --one-file-system --warning=no-file-ignored --warning=no-file-changed --warning=no-file-removed --use-compress-program=pigz var/yp --atime-preserve
Second try renamed /ourdomain/home-users-new.tgz to /ourdomain/home-users.tgz

Edit: I got some help from one of the maintainers of Gnu Tar and here was his response: "Exclude-related options are "position-sensitive"; you need to move the 'home/users' argument to the end of the command (after --exclude=aquota.user)."

So since we use a custom script and a config file, I'm just backing up aquota.user as a separate cron.

2

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .