src/t_dir_offset2: Add an option to find file by name
[xfstests-dev.git] / src / renameat2.c
1 /*
2  * Copyright (c) 2014, Miklos Szeredi <mszeredi@suse.cz>
3  * This file is published under GPL2+.
4  *
5  * This is a trivial wrapper around the renameat2 syscall.
6  */
7
8 #include "global.h"
9
10 #ifndef HAVE_RENAMEAT2
11 #include <sys/syscall.h>
12
13 #if !defined(SYS_renameat2) && defined(__x86_64__)
14 #define SYS_renameat2 316
15 #endif
16
17 #if !defined(SYS_renameat2) && defined(__i386__)
18 #define SYS_renameat2 353
19 #endif
20
21 static int renameat2(int dfd1, const char *path1,
22                      int dfd2, const char *path2,
23                      unsigned int flags)
24 {
25 #ifdef SYS_renameat2
26         return syscall(SYS_renameat2, dfd1, path1, dfd2, path2, flags);
27 #else
28         errno = ENOSYS;
29         return -1;
30 #endif
31 }
32 #endif
33
34 #ifndef RENAME_NOREPLACE
35 #define RENAME_NOREPLACE        (1 << 0)        /* Don't overwrite target */
36 #endif
37 #ifndef RENAME_EXCHANGE
38 #define RENAME_EXCHANGE         (1 << 1)        /* Exchange source and dest */
39 #endif
40 #ifndef RENAME_WHITEOUT
41 #define RENAME_WHITEOUT         (1 << 2)        /* Whiteout source */
42 #endif
43
44 int main(int argc, char *argv[])
45 {
46         int ret;
47         int c;
48         const char *path1 = NULL;
49         const char *path2 = NULL;
50         unsigned int flags = 0;
51         int test = 0;
52
53         for (c = 1; c < argc; c++) {
54                 if (argv[c][0] == '-') {
55                         switch (argv[c][1]) {
56                         case 't':
57                                 test = 1;
58                                 break;
59                         case 'n':
60                                 flags |= RENAME_NOREPLACE;
61                                 break;
62                         case 'x':
63                                 flags |= RENAME_EXCHANGE;
64                                 break;
65                         case 'w':
66                                 flags |= RENAME_WHITEOUT;
67                                 break;
68                         default:
69                                 goto usage;
70                         }
71                 } else if (!path1) {
72                         path1 = argv[c];
73                 } else if (!path2) {
74                         path2 = argv[c];
75                 } else {
76                         goto usage;
77                 }
78         }
79
80         if (!test && (!path1 || !path2))
81                 goto usage;
82
83         ret = renameat2(AT_FDCWD, path1, AT_FDCWD, path2, flags);
84         if (ret == -1) {
85                 if (test) {
86                         if (errno == ENOSYS || errno == EINVAL)
87                                 return 1;
88                         else
89                                 return 0;
90                 }
91                 /*
92                  * Turn EEXIST into ENOTEMPTY.  E.g. XFS uses EEXIST, and that
93                  * is also accepted by the standards.
94                  *
95                  * This applies only to plain rename and RENAME_WHITEOUT
96                  */
97                 if (errno == EEXIST && (!flags || (flags & RENAME_WHITEOUT)))
98                         errno = ENOTEMPTY;
99
100                 perror("");
101                 return 1;
102         }
103
104         return 0;
105
106 usage:
107         fprintf(stderr,
108                 "usage: %s [-t] [-n|-x|-w] path1 path2\n"
109                 "  -t  test\n"
110                 "  -n  noreplace\n"
111                 "  -x  exchange\n"
112                 "  -w  whiteout\n", argv[0]);
113
114         return 1;
115 }