| 1218 | |
| 1219 | #ifdef HAVE_SETATTRLIST |
| 1220 | int do_setattrlist_times(const char *path, STRUCT_STAT *stp) |
| 1221 | { |
| 1222 | extern int am_daemon, am_chrooted; |
| 1223 | struct attrlist attrList; |
| 1224 | struct timespec ts[2]; |
| 1225 | |
| 1226 | if (dry_run) return 0; |
| 1227 | RETURN_ERROR_IF_RO_OR_LO; |
| 1228 | |
| 1229 | /* setattrlist() takes a raw path and follows parent symlinks |
| 1230 | * (FSOPT_NOFOLLOW only blocks the final component). On a |
| 1231 | * daemon-no-chroot deployment, return ENOSYS so set_times()' |
| 1232 | * tier walk falls through to do_utimensat_at(), which routes |
| 1233 | * the timestamp update through a secure parent dirfd. The |
| 1234 | * macOS-specific attribute set this function would have used |
| 1235 | * (ATTR_CMN_MODTIME / ATTR_CMN_ACCTIME) is the same set |
| 1236 | * utimensat() handles, so no functionality is lost. */ |
| 1237 | if (am_daemon && !am_chrooted) { |
| 1238 | errno = ENOSYS; |
| 1239 | return -1; |
| 1240 | } |
| 1241 | |
| 1242 | /* Yes, this is in the opposite order of utime and similar. */ |
| 1243 | ts[0].tv_sec = stp->st_mtime; |
| 1244 | ts[0].tv_nsec = stp->ST_MTIME_NSEC; |
| 1245 | |
| 1246 | ts[1].tv_sec = stp->st_atime; |
| 1247 | ts[1].tv_nsec = stp->ST_ATIME_NSEC; |
| 1248 | |
| 1249 | memset(&attrList, 0, sizeof attrList); |
| 1250 | attrList.bitmapcount = ATTR_BIT_MAP_COUNT; |
| 1251 | attrList.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME; |
| 1252 | return setattrlist(path, &attrList, ts, sizeof ts, FSOPT_NOFOLLOW); |
| 1253 | } |
| 1254 | |
| 1255 | #ifdef SUPPORT_CRTIMES |
| 1256 | int do_setattrlist_crtime(const char *path, time_t crtime) |