ablog

不器用で落着きのない技術者のメモ

rsync: failed to set times on Operation not permitted

ぐぐってみると、

You must actually own a directory in order to set its access and modification times to an arbitrary value. Just having write permissions is not sufficient.

Either chown the directory, or don't use the -t or -a flags in rsync (-a includes -t). Note that this will have a small performance penalty since rsync will no longer be able to detect unmodified files and it will perform additional checking to see if they are different.

http://www.errorhelp.com/search/details/73579/rsync-failed-to-set-times-on-directory-operation-not-permitted

rsync のソースを見てみると、

$ uname -a
Darwin yohei-no-macbook-air.local 9.6.0 Darwin Kernel Version 9.6.0: Mon Nov 24 17:37:00 PST 2008; root:xnu-1228.9.59~1/RELEASE_I386 i386
$ cd  ~/Documents/src
$ git clone git://git.samba.org/rsync.git
Initialized empty Git repository in /Users/yohei/Documents/src/rsync/.git/
remote: Counting objects: 23252, done.
remote: Compressing objects: 100% (7519/7519), done.
remote: Total 23252 (delta 16217), reused 22275 (delta 15511)
Receiving objects: 100% (23252/23252), 4.51 MiB | 285 KiB/s, done.
Resolving deltas: 100% (16217/16217), done.
$ grep -R 'failed to set times on' *
rsync.c:			rsyserr(FERROR_XFER, errno, "failed to set times on %s",
$ less -N rsync.c
    492         if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && preserve_times == 1))
    493                 flags |= ATTRS_SKIP_MTIME;
    494         if (!(flags & ATTRS_SKIP_MTIME)
    495             && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
    496                 int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode);
    497                 if (ret < 0) {
    498                         rsyserr(FERROR_XFER, errno, "failed to set times on %s",
    499                                 full_fname(fname));
    500                         goto cleanup;
    501                 }
    502                 if (ret == 0) /* ret == 1 if symlink could not be set */
    503                         updated = 1;
    504                 else
    505                         file->flags |= FLAG_TIME_FAILED;
    506         }
$ grep -R set_modtime *
generator.c:				set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode);
rsync.c:		int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode);
util.c:int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
$ less -N util.c 
    126 int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
    127 {
    128 #ifndef CAN_SET_SYMLINK_TIMES
    129         if (S_ISLNK(mode))
    130                 return 1;
    131 #endif
    132 
    133         if (DEBUG_GTE(TIME, 1)) {
    134                 rprintf(FINFO, "set modtime of %s to (%ld) %s",
    135                         fname, (long)modtime,
    136                         asctime(localtime(&modtime)));
    137         }
    138 
    139         if (dry_run)
    140                 return 0;
    141 
    137         }
    138 
    139         if (dry_run)
    140                 return 0;
    141 
    142         {
    143 #ifdef HAVE_UTIMENSAT
    144                 struct timespec t[2];
    145                 t[0].tv_sec = 0;
    146                 t[0].tv_nsec = UTIME_NOW;
    147                 t[1].tv_sec = modtime;
    148                 t[1].tv_nsec = mod_nsec;
    149                 if (utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW) < 0)
    150                         return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1;
    151                 return 0;
    152 #elif defined HAVE_UTIMES || defined HAVE_LUTIMES
    153                 struct timeval t[2];
    154                 t[0].tv_sec = time(NULL);
    155                 t[0].tv_usec = 0;
    156                 t[1].tv_sec = modtime;
    157                 t[1].tv_usec = mod_nsec / 1000;
    158 # ifdef HAVE_LUTIMES
    159                 if (lutimes(fname, t) < 0)
    160                         return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1;
    161                 return 0;
    162 # else
    163                 return utimes(fname, t);
    164 # endif
    165 #elif defined HAVE_STRUCT_UTIMBUF
    166                 struct utimbuf tbuf;
    167                 tbuf.actime = time(NULL);
    168                 tbuf.modtime = modtime;
    169                 return utime(fname,&tbuf);
    170 #elif defined HAVE_UTIME
    171                 time_t t[2];
    172                 t[0] = time(NULL);
    173                 t[1] = modtime;
    174                 return utime(fname,t);
    175 #else
    176 #error No file-time-modification routine found!
    177 #endif
    178         }
    179 }
    180 

utime/utimes でエラーになってるぽいので、ぐぐってみると、

説明
utime ()は filename で示される inode のアクセス時刻と修正時刻を buf 中の actime と modtime にそれぞれ変更する。
buf が NULL の場合、ファイルのアクセス時刻と修正時刻は現在の時刻に設定される。
タイムスタンプの変更は以下の場合に許可される: プロセスに適切な特権がある (Linux では CAP_FOWNER ケーパビリティ (capability) がある)。 または実効 (effective) ユーザ ID が、ファイルのユーザ ID と等しい。 または buf が NULL で、かつプロセスがファイルへの書き込み許可を持っている。

utime - システムコールの説明 - Linux コマンド集 一覧表

The utime() system call changes the access and modification times of the inode
specified by filename to the actime and modtime fields of times respectively.

If times is NULL, then the access and modification times of the file are set
to the current time.

Changing timestamps is permitted when: either the process has appropriate
privileges, or the effective user ID equals the user ID of the file, or times
is NULL and the process has write permission for the file.

utime(2) - Linux manual page

つぶやいていたら、親切な人からアドバイスが。

@yoheia http://bit.ly/arbitr you must actually own a directory in order to set its access and modification times to a definite value. (con

Twitter / Account Suspended

ソースとターゲットで完全同期するなら、chown。そうじゃなければ、rsync で -t、-a オプションを使わないようにする。オプションの変更が難しい場合は、ターゲットで動く処理の実効ユーザを rsync を実行するユーザと合わせるとかか。