/* * Copyright (c) 2014, Miklos Szeredi * This file is published under GPL2+. * * This is a trivial wrapper around the renameat2 syscall. */ #include "global.h" #ifndef HAVE_RENAMEAT2 #include #if !defined(SYS_renameat2) && defined(__x86_64__) #define SYS_renameat2 316 #endif #if !defined(SYS_renameat2) && defined(__i386__) #define SYS_renameat2 353 #endif static int renameat2(int dfd1, const char *path1, int dfd2, const char *path2, unsigned int flags) { #ifdef SYS_renameat2 return syscall(SYS_renameat2, dfd1, path1, dfd2, path2, flags); #else errno = ENOSYS; return -1; #endif } #endif #ifndef RENAME_NOREPLACE #define RENAME_NOREPLACE (1 << 0) /* Don't overwrite target */ #endif #ifndef RENAME_EXCHANGE #define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */ #endif #ifndef RENAME_WHITEOUT #define RENAME_WHITEOUT (1 << 2) /* Whiteout source */ #endif int main(int argc, char *argv[]) { int ret; int c; const char *path1 = NULL; const char *path2 = NULL; unsigned int flags = 0; int test = 0; for (c = 1; c < argc; c++) { if (argv[c][0] == '-') { switch (argv[c][1]) { case 't': test = 1; break; case 'n': flags |= RENAME_NOREPLACE; break; case 'x': flags |= RENAME_EXCHANGE; break; case 'w': flags |= RENAME_WHITEOUT; break; default: goto usage; } } else if (!path1) { path1 = argv[c]; } else if (!path2) { path2 = argv[c]; } else { goto usage; } } if (!test && (!path1 || !path2)) goto usage; ret = renameat2(AT_FDCWD, path1, AT_FDCWD, path2, flags); if (ret == -1) { if (test) { if (errno == ENOSYS || errno == EINVAL) return 1; else return 0; } /* * Turn EEXIST into ENOTEMPTY. E.g. XFS uses EEXIST, and that * is also accepted by the standards. * * This applies only to plain rename and RENAME_WHITEOUT */ if (errno == EEXIST && (!flags || (flags & RENAME_WHITEOUT))) errno = ENOTEMPTY; perror(""); return 1; } return 0; usage: fprintf(stderr, "usage: %s [-t] [-n|-x|-w] path1 path2\n" " -t test\n" " -n noreplace\n" " -x exchange\n" " -w whiteout\n", argv[0]); return 1; }