10#include <sys/syscall.h>
12#define CHECK(condition) \
15 fprintf(stderr, "RENAME-FLAGS-CONTRACT: line=%d errno=%d\n", __LINE__, errno); \
23 pthread_barrier_t* barrier;
27static void* rename_racer(
void* opaque) {
29 pthread_barrier_wait(
race->barrier);
31 renameat2(
race->directory,
race->source,
race->directory,
"winner", RENAME_NOREPLACE);
36static int create_byte(
int directory,
const char* name,
unsigned char value) {
37 int fd = openat(directory, name, O_CREAT | O_EXCL | O_RDWR, 0600);
40 int success = write(fd, &value, 1) == 1;
42 return success ? 0 : -1;
45int main(
int argc,
char** argv) {
46 const char* parent = argc > 1 ? argv[1] :
"/tmp";
47 char path[512], absolute[640];
48 snprintf(path,
sizeof(path),
"%s/rename-flags-%ld", parent, (
long)getpid());
49 int directory = -1, retained = -1, result = 1;
50 struct stat source, target, status;
51 unsigned char byte = 0;
52 CHECK(mkdir(path, 0700) == 0);
53 directory = open(path, O_RDONLY | O_DIRECTORY);
54 CHECK(directory >= 0);
55 CHECK(create_byte(directory,
"source", 31) == 0 && create_byte(directory,
"target", 47) == 0);
56 CHECK(fstatat(directory,
"source", &source, 0) == 0);
57 CHECK(fstatat(directory,
"target", &target, 0) == 0);
58 retained = openat(directory,
"source", O_RDONLY);
61 CHECK(renameat2(directory,
"source", directory,
"target", RENAME_NOREPLACE) == -1 &&
63 CHECK(fstatat(directory,
"source", &status, 0) == 0 && status.st_ino == source.st_ino);
64 CHECK(fstatat(directory,
"target", &status, 0) == 0 && status.st_ino == target.st_ino);
66 CHECK(renameat2(directory,
"source", directory,
"source", RENAME_NOREPLACE) == -1 &&
68 if (argc > 2 && !strcmp(argv[2],
"--hard-links")) {
69 CHECK(linkat(directory,
"source", directory,
"alias", 0) == 0);
71 CHECK(renameat2(directory,
"source", directory,
"alias", RENAME_NOREPLACE) == -1 &&
73 CHECK(unlinkat(directory,
"alias", 0) == 0);
76 CHECK(renameat2(directory,
"source", directory,
"moved", RENAME_NOREPLACE) == 0);
77 CHECK(fstatat(directory,
"moved", &status, 0) == 0 && status.st_ino == source.st_ino);
78 CHECK(pread(retained, &
byte, 1, 0) == 1 &&
byte == 31);
80 CHECK(fstatat(directory,
"source", &status, 0) == -1 && errno == ENOENT);
81 CHECK(syscall(SYS_renameat2, directory,
"moved", directory,
"target", 0) == 0);
82 CHECK(fstatat(directory,
"target", &status, 0) == 0 && status.st_ino == source.st_ino);
83 snprintf(absolute,
sizeof(absolute),
"%s/absolute", path);
84 CHECK(renameat2(directory,
"target", -1, absolute, RENAME_NOREPLACE) == 0);
85 CHECK(mkdirat(directory,
"nested", 0700) == 0);
86 CHECK(renameat2(directory,
"absolute", directory,
"nested/file", RENAME_NOREPLACE) == 0);
89 CHECK(renameat2(directory,
"nested/file", directory,
"other", RENAME_EXCHANGE) == -1 &&
92 CHECK(renameat2(directory,
"nested/file", directory,
"other", RENAME_WHITEOUT) == -1 &&
95 CHECK(renameat2(directory,
"nested/file", directory,
"other",
96 RENAME_EXCHANGE | RENAME_NOREPLACE) == -1 &&
99 CHECK(renameat2(directory,
"nested/file", directory,
"other", 8) == -1 && errno == EINVAL);
101 CHECK(renameat2(-1,
"nested/file", directory,
"other", RENAME_NOREPLACE) == -1 && errno == EBADF);
103 CHECK(renameat2(directory,
"", directory,
"other", RENAME_NOREPLACE) == -1 && errno == ENOENT);
105 for (
unsigned iteration = 0; iteration < 16; ++iteration) {
106 CHECK(create_byte(directory,
"first", 61) == 0 && create_byte(directory,
"second", 83) == 0);
107 pthread_barrier_t barrier;
109 CHECK(pthread_barrier_init(&barrier, NULL, 2) == 0);
110 struct race races[2] = {{directory,
"first", &barrier, -2, 0},
111 {directory,
"second", &barrier, -2, 0}};
112 const int created = pthread_create(&thread, NULL, rename_racer, &races[0]);
114 pthread_barrier_destroy(&barrier);
118 rename_racer(&races[1]);
119 CHECK(pthread_join(thread, NULL) == 0);
120 CHECK(pthread_barrier_destroy(&barrier) == 0);
121 CHECK((races[0].result == 0 && races[1].result == -1 && races[1].error == EEXIST) ||
122 (races[1].result == 0 && races[0].result == -1 && races[0].error == EEXIST));
123 CHECK(unlinkat(directory,
"winner", 0) == 0);
124 CHECK(unlinkat(directory, races[0].result ?
"first" :
"second", 0) == 0);
131 if (directory >= 0) {
132 const char* names[] = {
"source",
"target",
"moved",
"absolute",
"nested/file",
133 "other",
"first",
"second",
"winner",
"alias"};
134 for (
unsigned i = 0; i <
sizeof(names) /
sizeof(names[0]); ++i)
135 unlinkat(directory, names[i], 0);
136 unlinkat(directory,
"nested", AT_REMOVEDIR);
141 puts(
"RENAME-FLAGS-CONTRACT: PASS");