1 // SPDX-License-Identifier: CDDL-1.0
2 /* SPDX-License-Identifier: CDDL-1.0 OR MPL-2.0 */
3 /*
4 * CDDL HEADER START
5 *
6 * The contents of this file are subject to the terms of the
7 * Common Development and Distribution License (the "License").
8 * You may not use this file except in compliance with the License.
9 *
10 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11 * or https://opensource.org/licenses/CDDL-1.0.
12 * See the License for the specific language governing permissions
13 * and limitations under the License.
14 *
15 * When distributing Covered Code, include this CDDL HEADER in each
16 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 * If applicable, add the following below this CDDL HEADER, with the
18 * fields enclosed by brackets "[]" replaced with your own identifying
19 * information: Portions Copyright [yyyy] [name of copyright owner]
20 *
21 * CDDL HEADER END
22 */
23
24 /*
25 * Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com>
26 * Copyright (C) 2019 SUSE LLC
27 */
28
29 /*
30 * mv(1) doesn't currently support RENAME_{EXCHANGE,WHITEOUT} so this is a very
31 * simple renameat2(2) wrapper for the OpenZFS self-tests.
32 */
33
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/syscall.h>
41
42 #ifndef SYS_renameat2
43 #ifdef __NR_renameat2
44 #define SYS_renameat2 __NR_renameat2
45 #elif defined(__x86_64__)
46 #define SYS_renameat2 316
47 #elif defined(__i386__)
48 #define SYS_renameat2 353
49 #elif defined(__arm__) || defined(__aarch64__)
50 #define SYS_renameat2 382
51 #else
52 #error "SYS_renameat2 not known for this architecture."
53 #endif
54 #endif
55
56 #ifndef RENAME_NOREPLACE
57 #define RENAME_NOREPLACE (1 << 0) /* Don't overwrite target */
58 #endif
59 #ifndef RENAME_EXCHANGE
60 #define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
61 #endif
62 #ifndef RENAME_WHITEOUT
63 #define RENAME_WHITEOUT (1 << 2) /* Whiteout source */
64 #endif
65
66 /* glibc doesn't provide renameat2 wrapper, let's use our own */
67 static int
sys_renameat2(int olddirfd,const char * oldpath,int newdirfd,const char * newpath,unsigned int flags)68 sys_renameat2(int olddirfd, const char *oldpath,
69 int newdirfd, const char *newpath, unsigned int flags)
70 {
71 int ret = syscall(SYS_renameat2, olddirfd, oldpath, newdirfd, newpath,
72 flags);
73 return ((ret < 0) ? -errno : ret);
74 }
75
76 static void
usage(void)77 usage(void)
78 {
79 fprintf(stderr, "usage: renameat2 [-Cnwx] src dst\n");
80 exit(1);
81 }
82
83 static void
check(void)84 check(void)
85 {
86 int err = sys_renameat2(AT_FDCWD, ".", AT_FDCWD, ".", RENAME_EXCHANGE);
87 exit(err == -ENOSYS);
88 }
89
90 int
main(int argc,char ** argv)91 main(int argc, char **argv)
92 {
93 char *src, *dst;
94 int ch, err;
95 unsigned int flags = 0;
96
97 while ((ch = getopt(argc, argv, "Cnwx")) >= 0) {
98 switch (ch) {
99 case 'C':
100 check();
101 break;
102 case 'n':
103 flags |= RENAME_NOREPLACE;
104 break;
105 case 'w':
106 flags |= RENAME_WHITEOUT;
107 break;
108 case 'x':
109 flags |= RENAME_EXCHANGE;
110 break;
111 default:
112 usage();
113 break;
114 }
115 }
116
117 argc -= optind;
118 argv += optind;
119
120 if (argc != 2)
121 usage();
122 src = argv[0];
123 dst = argv[1];
124
125 err = sys_renameat2(AT_FDCWD, src, AT_FDCWD, dst, flags);
126 if (err < 0)
127 fprintf(stderr, "renameat2: %s", strerror(-err));
128 return (err != 0);
129 }
130