Bug 4621 - Option to create ancestors of destination, like "mkdir -p"
Summary: Option to create ancestors of destination, like "mkdir -p"
Status: RESOLVED FIXED
Alias: None
Product: rsync
Classification: Unclassified
Component: core (show other bugs)
Version: 3.0.0
Hardware: All Linux
: P3 enhancement (vote)
Target Milestone: ---
Assignee: Wayne Davison
QA Contact: Rsync QA Contact
URL:
Keywords:
: 8162 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-05-15 14:48 UTC by Maxim Veksler
Modified: 2020-07-24 03:57 UTC (History)
5 users (show)

See Also:


Attachments
change default creation of directories to work just like "mkdir -p" (1.43 KB, patch)
2012-02-06 06:32 UTC, Carlo Marcelo Arenas Belon
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Maxim Veksler 2007-05-15 14:48:11 UTC
Hi,

I use rsync as part of my installation procedure. In times I need to create several hierarchies of directories, it would be useful IMHO if I could run rsync as :
"""
rsync -r -p doc /opt/FooDocDir/v5.3/
"""
which would create /opt/FooDocDir/v5.3/ and copy doc/* to /opt/FooDocDir/v5.3/doc/*


Maxim.
Comment 1 Maxim Veksler 2007-09-04 13:32:58 UTC
Hi,

I would like to add that I've created a wrapper for rsync to implemented this functionality in the mean time, if/when it will be available via rsync itself.

Bash:

"""
function cp() {
# PARAMS:
#       $1 : What to Sync?
#       $2 : Where To?
#
        if [ $# -lt 1 ]; then
                echo "Usage: "
                echo " cp <source> <target>"
                echo " cp <source>"
                echo " cp <source> /absolute/path/to/file"
                exit 1
        fi

        # The following is a very strange way to load last variable into RSYNC_TARGET (don't ask...)
        for RSYNC_TARGET; do :; done

        # If the destination we got is not /absolute/path/to/somewhere, append $TARGET_DIR
        #  infact, if we got empty target, assume root of TARGET_DIR

        # If we get full path - assume user tell us where to put it
        if [ "${RSYNC_TARGET:0:1}" != '/' ]; then
                RSYNC_TARGET="$TARGET_DIR/$RSYNC_TARGET"
        # Else if we got only 1 parameter, in this case - just take the file name and store it under build root dir.
        elif [ $# -eq 1 ]; then
                RSYNC_TARGET="${TARGET_DIR}/${1##*/}"
        fi

        RSYNC_SOURCE="$1"

        # Are we syncing directory as source?
        # If we are syncing directory, then we need to make sure it exists for rsync not fail.
        # If we are syncing multiple file selection, the destination must be a directory.

        # If we are syncing multiple file selection, the destination must be a directory.
        if [ -d "$RSYNC_SOURCE" -o $# -gt 2 ]; then
                # If target directory does not exists, create it.
                if [ ! -d "$RSYNC_TARGET" ]; then
                        mkdir -p "$RSYNC_TARGET"
                fi
        else # Anything other then a directory, make sure the dir path exists.
                if [ ! -d "${RSYNC_TARGET%/*}" ]; then
                        mkdir -p "${RSYNC_TARGET%/*}"
                fi
        fi


        # Load $1..$$-1 into bash array. Why? because this is <long param with-spaces> safe.
        declare -a RSYNC_ACTION
        local RSYNC_ACTION

        # If we got only 1 parameter (only source).
        if [ $# -eq 1 ]; then
                RSYNC_ACTION[0]="$1"
                RSYNC_ACTION[1]="$RSYNC_TARGET"
        else
        # for more then one... load them all into array.
                for (( i=0; $# > 1; i++ ))
                do
                        RSYNC_ACTION[$i]="$1"
                        shift
                done
                RSYNC_ACTION[i++]="$RSYNC_TARGET"
        fi

        rsync --recursive --quiet --links --safe-links --hard-links --perms --times --one-file-system --exclude='.svn' "${RSYNC_ACTION[@]}"
}
Comment 2 Topher Kessler 2008-07-12 12:01:56 UTC
This would be an EXCEPTIONALLY useful command/option to have implemented in rsync. I am currently trying to implement a backup system which writes to an available module called "rsyncbackup" on an rsync server. I want this module to be writable via rsync, but have it be read-only through SMB and other file server protocols. I use the following command to invoke rsync:

rsync -rtglv --delete --delete-after --delete-excluded --progress --backup --backup-dir=../../backups/$HOSTNAME/$DAYDATE/$HOURMINSEC/ ~/$USERNAME server.domain.name::rsyncbackup/$USERNAME/current/$HOSTNAME

In my rsyncd.conf file, I have this entry for the "rsyncbackup" module:

[rsyncbackup]
        path = /Volumes/Burns1/rsyncbackup
        hosts allow = ip_address
        list = true
        exclude from = /Volumes/Burns1/rsyncbackup/info/rsyncexclude.txt
        auth users =
        use chroot = yes
        uid = serveradmin
        gid = backupgrp
        read only = false
        write only = true
        comment = rsync backup directory

The "rsyncbackup" folder itself is given these permissions:
        serveradmin: read & write
        backupgrp: read only
        others: none

The "rsyncbackup" destination module starts out empty, and when I run the command on a client computer it gives an error such as:

        rsync: mkdir "username/current/hostname" (in rsyncbackup) failed: No such file or directory (2).

All paths in the rsyncd.conf file exist, and the command works very well if I leave out the "current/$HOSTNAME" part, but defining nonexistent subfolders to back up to causes this error and rsync doesnt continue. The only way around is to manually create the folders.

It appears that rsync is invoking the "mkdir" command without the "-p" option, which limits its folder creation to only one directory, instead of allowing it to create a tree of directories. It would be exceptionally useful if rsync would supply the "-p" option to the mkdir command so it will create a new tree of folders and use that as the backup destination. Currently, I have to work around this by sharing the rsync module with read and write permissions, and have the command run on client computers via SMB to transfer files to the server (bypassing the rsync server and using samba file sharing instead as the means for transfer).
Comment 3 Matt McCutchen 2008-07-12 20:58:56 UTC
If this option is added to rsync, I suggest calling it --create-dest-parents because -p is already taken.

(In reply to comment #2)
> It appears that rsync is invoking the "mkdir" command without the "-p" option,
> which limits its folder creation to only one directory, instead of allowing it
> to create a tree of directories.

Note that rsync uses the mkdir(2) system call, not the mkdir(1) command, so it would have to loop over the parents of the destination itself.

Two currently supported workarounds:

1. Cut off the portion of the destination path that needs to be created, and instead prepend it to the file-list paths of all the source files by accessing the source through an appropriately named symlink with --relative.

2. Run a "mkdir -p" command before rsync.  If the destination is local, use a wrapper like the one in comment #1; if it's over remote shell, use --rsync-path; and if it's on a daemon, define a "pre-xfer exec" script that determines the destination directory from the RSYNC_* environment variables and creates it.
Comment 4 moggie 2009-12-15 06:12:29 UTC
Greetings,

I just noticed my rsync backup scripts failing and discovered it was due to a missing ancestor of the remote target directory. I immediately searched for a tunable that would signal rsync to use 'mkdir -p' instead of just 'mkdir' when initially creating the target directory, but didn't seem to find one.

I agree with everyone else that it would be incredibly useful for rsync to either adopt such behaviour by default (assuming always doing 'mkdir -p' isn't harmful in any way), or have a tunable to enable it.

Kind regards,
moggie
Comment 5 Matt McCutchen 2011-05-24 17:52:35 UTC
*** Bug 8162 has been marked as a duplicate of this bug. ***
Comment 6 yannick 2011-05-24 18:18:00 UTC
Ok but in file define in pre-xfer exec = post-exec.sh

!#bin/sh/...


.... why here for mkdir -p ....
Comment 7 Carlo Marcelo Arenas Belon 2012-02-06 06:32:17 UTC
Created attachment 7294 [details]
change default creation of directories to work just like "mkdir -p"

tested on linux (amd64), but likely to work fine in all other *nix at least
`make check` passes all tests, and since I couldn't find any reason why this couldn't be the default mode since it will be only triggered as a fallback and will be likely working otherwise the same, has been implemented without a flag
Comment 9 Wayne Davison 2020-07-24 03:57:10 UTC
I have added an option named --mkpath that tells rsync to create the path component of the destination arg if it does not exist.

If you want that to be the default, consider creating a popt alias in ~/.popt or /etc/popt such as:

rsync alias -a -a --mkpath

which would add it anytime you use the -a option.