+
+int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
+ _cleanup_free_ char *t = NULL;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ r = tempfn_random(to, &t);
+ if (r < 0)
+ return r;
+
+ r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
+ if (r < 0)
+ return r;
+
+ if (replace) {
+ r = renameat(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0)
+ r = -errno;
+ } else
+ r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0) {
+ (void) unlink_noerrno(t);
+ return r;
+ }
+
+ return 0;
+}
+
+int copy_times(int fdf, int fdt) {
+ struct timespec ut[2];
+ struct stat st;
+ usec_t crtime = 0;
+
+ assert(fdf >= 0);
+ assert(fdt >= 0);
+
+ if (fstat(fdf, &st) < 0)
+ return -errno;
+
+ ut[0] = st.st_atim;
+ ut[1] = st.st_mtim;
+
+ if (futimens(fdt, ut) < 0)
+ return -errno;
+
+ if (fd_getcrtime(fdf, &crtime) >= 0)
+ (void) fd_setcrtime(fdt, crtime);
+
+ return 0;
+}
+
+int copy_xattr(int fdf, int fdt) {
+ _cleanup_free_ char *bufa = NULL, *bufb = NULL;
+ size_t sza = 100, szb = 100;
+ ssize_t n;
+ int ret = 0;
+ const char *p;
+
+ for (;;) {
+ bufa = malloc(sza);
+ if (!bufa)
+ return -ENOMEM;
+
+ n = flistxattr(fdf, bufa, sza);
+ if (n == 0)
+ return 0;
+ if (n > 0)
+ break;
+ if (errno != ERANGE)
+ return -errno;
+
+ sza *= 2;
+
+ free(bufa);
+ bufa = NULL;
+ }
+
+ p = bufa;
+ while (n > 0) {
+ size_t l;
+
+ l = strlen(p);
+ assert(l < (size_t) n);
+
+ if (startswith(p, "user.")) {
+ ssize_t m;
+
+ if (!bufb) {
+ bufb = malloc(szb);
+ if (!bufb)
+ return -ENOMEM;
+ }
+
+ m = fgetxattr(fdf, p, bufb, szb);
+ if (m < 0) {
+ if (errno == ERANGE) {
+ szb *= 2;
+ free(bufb);
+ bufb = NULL;
+ continue;
+ }
+
+ return -errno;
+ }
+
+ if (fsetxattr(fdt, p, bufb, m, 0) < 0)
+ ret = -errno;
+ }
+
+ p += l + 1;
+ n -= l + 1;
+ }
+
+ return ret;
+}