Fix: statements with side-effects in assert statements
[lttng-tools.git] / tests / unit / test_fd_tracker.c
1 /*
2 * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include <stdlib.h>
9 #include <inttypes.h>
10 #include <stdbool.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <stdarg.h>
14 #include <tap/tap.h>
15 #include <sys/types.h>
16 #include <dirent.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23
24 #include <urcu.h>
25
26 #include <common/compat/directory-handle.h>
27 #include <common/error.h>
28 #include <common/fd-tracker/fd-tracker.h>
29
30 /* For error.h */
31 int lttng_opt_quiet = 1;
32 int lttng_opt_verbose;
33 int lttng_opt_mi;
34
35 /* Number of TAP tests in this file */
36 #define NUM_TESTS 61
37 /* 3 for stdin, stdout, and stderr */
38 #define STDIO_FD_COUNT 3
39 #define TRACKER_FD_LIMIT 50
40 #define TMP_DIR_PATTERN "/tmp/fd-tracker-XXXXXX"
41 #define TEST_UNLINK_DIRECTORY_NAME "unlinked_files"
42
43 /*
44 * Count of fds, beyond stdin, stderr, stdout that were open
45 * at the launch of the test. This allows the test to succeed when
46 * run by automake's test runner or valgrind which both open
47 * fds behind our back.
48 */
49 int unknown_fds_count;
50
51 const char file_contents[] = "Bacon ipsum dolor amet jerky drumstick sirloin "
52 "strip steak venison boudin filet mignon picanha doner shoulder. "
53 "Strip steak brisket alcatra, venison beef chuck cupim pastrami. "
54 "Landjaeger tri-tip salami leberkas ball tip, ham hock chuck sausage "
55 "flank jerky cupim. Pig bacon chuck pancetta andouille.";
56
57 static
58 void get_temporary_directories(char **_test_directory, char **_unlink_directory)
59 {
60 int ret;
61 char tmp_path_pattern[] = TMP_DIR_PATTERN;
62 char *output_dir;
63
64 output_dir = mkdtemp(tmp_path_pattern);
65 if (!output_dir) {
66 diag("Failed to create temporary path of the form %s",
67 TMP_DIR_PATTERN);
68 assert(0);
69 }
70
71 *_test_directory = strdup(output_dir);
72 assert(*_test_directory);
73 ret = asprintf(_unlink_directory, "%s/%s", output_dir,
74 TEST_UNLINK_DIRECTORY_NAME);
75 if (ret < 0) {
76 assert(0);
77 }
78 }
79
80 static
81 int fd_count(void)
82 {
83 DIR *dir;
84 struct dirent *entry;
85 int count = 0;
86
87 dir = opendir("/proc/self/fd");
88 if (!dir) {
89 perror("# Failed to enumerate /proc/self/fd/ to count the number of used file descriptors");
90 count = -1;
91 goto end;
92 }
93
94 while ((entry = readdir(dir)) != NULL) {
95 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
96 continue;
97 }
98 count++;
99 }
100 /* Don't account for the file descriptor opened by opendir(). */
101 count--;
102 if (closedir(dir)) {
103 perror("# Failed to close test program's self/fd directory file descriptor");
104 }
105 end:
106 return count;
107 }
108
109 static
110 void check_fd_count(int expected_count)
111 {
112 int count = 0;
113
114 count = fd_count();
115 ok(count == expected_count, "Expected %d open file descriptors (%d are open)",
116 expected_count, count);
117 }
118
119 static
120 int noop_open(void *data, int *fds)
121 {
122 *fds = *((int *) data);
123 return 0;
124 }
125
126 static
127 int noop_close(void *data, int *fds)
128 {
129 return 0;
130 }
131
132 static
133 void track_std_fds(struct fd_tracker *tracker)
134 {
135 int i;
136 struct { int fd; const char *name; } files[] = {
137 { .fd = fileno(stdin), .name = "stdin" },
138 { .fd = fileno(stdout), .name = "stdout" },
139 { .fd = fileno(stderr), .name = "stderr" },
140 };
141
142 for (i = 0; i < sizeof(files) / sizeof(*files); i++) {
143 int out_fd, ret;
144
145 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
146 &files[i].name, 1, noop_open, &files[i].fd);
147 assert(out_fd == files[i].fd);
148
149 ok(ret == 0, "Track unsuspendable fd %d (%s)", files[i].fd,
150 files[i].name);
151 }
152 }
153
154 static
155 void untrack_std_fds(struct fd_tracker *tracker)
156 {
157 int i;
158 struct { int fd; const char *name; } files[] = {
159 { .fd = fileno(stdin), .name = "stdin" },
160 { .fd = fileno(stdout), .name = "stdout" },
161 { .fd = fileno(stderr), .name = "stderr" },
162 };
163 unsigned int fds_set_to_minus_1 = 0;
164
165 for (i = 0; i < sizeof(files) / sizeof(*files); i++) {
166 int fd = files[i].fd;
167 int ret = fd_tracker_close_unsuspendable_fd(tracker,
168 &files[i].fd, 1, noop_close, NULL);
169
170 ok(ret == 0, "Untrack unsuspendable fd %d (%s)", fd,
171 files[i].name);
172 fds_set_to_minus_1 += (files[i].fd == -1);
173 }
174 }
175
176 /*
177 * Basic test opening and closing three unsuspendable fds.
178 */
179 static
180 void test_unsuspendable_basic(void)
181 {
182 int ret;
183 struct fd_tracker *tracker;
184 char *test_directory = NULL, *unlinked_files_directory = NULL;
185
186 get_temporary_directories(&test_directory, &unlinked_files_directory);
187
188 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
189 ok(tracker, "Created an fd tracker with a limit of %d simulateously opened file descriptors",
190 TRACKER_FD_LIMIT);
191 if (!tracker) {
192 goto end;
193 }
194
195 track_std_fds(tracker);
196 untrack_std_fds(tracker);
197
198 fd_tracker_destroy(tracker);
199 ret = rmdir(test_directory);
200 ok(ret == 0, "Test directory is empty");
201 end:
202 free(test_directory);
203 free(unlinked_files_directory);
204 }
205
206 static
207 int error_open(void *data, int *fds)
208 {
209 return *((int *) data);
210 }
211
212 static
213 int error_close(void *data, int *fds)
214 {
215 return *((int *) data);
216 }
217
218 /*
219 * Validate that user callback return values are returned to the
220 * caller of the fd tracker.
221 */
222 static
223 void test_unsuspendable_cb_return(void)
224 {
225 int ret, stdout_fd = fileno(stdout), out_fd = 42;
226 struct fd_tracker *tracker;
227 int expected_error = -ENETDOWN;
228 char *test_directory = NULL, *unlinked_files_directory = NULL;
229
230 get_temporary_directories(&test_directory, &unlinked_files_directory);
231
232 tracker = fd_tracker_create(test_directory, TRACKER_FD_LIMIT);
233 assert(tracker);
234
235 /* The error_open callback should fail and return 'expected_error'. */
236 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
237 NULL, 1, error_open, &expected_error);
238 ok(ret == expected_error, "fd_tracker_open_unsuspendable_fd() forwards the user callback's error code");
239 ok(out_fd == 42, "Output fd parameter is unaffected on error of fd_tracker_open_unsuspendable_fd()");
240
241 /*
242 * Track a valid fd since we don't want the tracker to fail with an
243 * invalid fd error for this test.
244 */
245 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
246 NULL, 1, noop_open, &stdout_fd);
247 ok(out_fd == stdout_fd, "fd_tracker_open_unsuspendable_fd() sets the output fd parameter to the newly-tracked fd's value");
248 assert(!ret);
249
250 ret = fd_tracker_close_unsuspendable_fd(tracker,
251 &stdout_fd, 1, error_close, &expected_error);
252 ok(ret == expected_error, "fd_tracker_close_unsuspendable_fd() forwards the user callback's error code");
253 ret = fd_tracker_close_unsuspendable_fd(tracker,
254 &stdout_fd, 1, noop_close, &expected_error);
255 assert(!ret);
256
257 fd_tracker_destroy(tracker);
258 ret = rmdir(test_directory);
259 ok(ret == 0, "Test directory is empty");
260 free(test_directory);
261 free(unlinked_files_directory);
262 }
263
264 /*
265 * Validate that the tracker refuses to track two identical unsuspendable
266 * file descriptors.
267 */
268 static
269 void test_unsuspendable_duplicate(void)
270 {
271 int ret, stdout_fd = fileno(stdout), out_fd;
272 struct fd_tracker *tracker;
273 char *test_directory = NULL, *unlinked_files_directory = NULL;
274
275 get_temporary_directories(&test_directory, &unlinked_files_directory);
276
277 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
278 assert(tracker);
279
280 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
281 NULL, 1, noop_open, &stdout_fd);
282 assert(!ret);
283 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
284 NULL, 1, noop_open, &stdout_fd);
285 ok(ret == -EEXIST, "EEXIST reported on open of an already tracked file descriptor");
286
287 ret = fd_tracker_close_unsuspendable_fd(tracker,
288 &stdout_fd, 1, noop_close, NULL);
289 assert(!ret);
290
291 fd_tracker_destroy(tracker);
292 ret = rmdir(test_directory);
293 ok(ret == 0, "Test directory is empty");
294 free(test_directory);
295 free(unlinked_files_directory);
296 }
297
298 static
299 int open_pipes(void *data, int *out_fds)
300 {
301 unsigned int i;
302 const unsigned int pipe_count = TRACKER_FD_LIMIT / 2;
303
304 for (i = 0; i < pipe_count; i++) {
305 int ret = pipe(&out_fds[i * 2]);
306
307 if (ret) {
308 return -errno;
309 }
310 }
311 return 0;
312 }
313
314 static
315 int close_pipes(void *data, int *fds)
316 {
317 int i;
318 int *pipes = fds;
319
320 for (i = 0; i < TRACKER_FD_LIMIT; i++) {
321 int ret = close(pipes[i]);
322
323 if (ret) {
324 return -errno;
325 }
326 }
327 return 0;
328 }
329
330 /*
331 * Validate that the tracker enforces the open file descriptor limit
332 * when unsuspendable file descriptors are being opened.
333 */
334 static
335 void test_unsuspendable_limit(void)
336 {
337 struct fd_tracker *tracker;
338 int ret, stdout_fd = fileno(stdout), out_fd;
339 int fds[TRACKER_FD_LIMIT];
340 char *test_directory = NULL, *unlinked_files_directory = NULL;
341
342 get_temporary_directories(&test_directory, &unlinked_files_directory);
343
344 /* This test assumes TRACKER_FD_LIMIT is a multiple of 2. */
345 assert((TRACKER_FD_LIMIT % 2 == 0) && TRACKER_FD_LIMIT);
346
347 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
348 assert(tracker);
349
350 ret = fd_tracker_open_unsuspendable_fd(tracker, fds,
351 NULL, TRACKER_FD_LIMIT, open_pipes, NULL);
352 ok(ret == 0, "File descriptor tracker allowed the user to meet its limit with unsuspendable file descriptors (%d)",
353 TRACKER_FD_LIMIT);
354
355 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
356 NULL, 1, noop_open, &stdout_fd);
357 ok(ret == -EMFILE, "EMFILE reported when exceeding the file descriptor limit while opening an unsuspendable fd");
358
359 ret = fd_tracker_close_unsuspendable_fd(tracker,
360 fds, TRACKER_FD_LIMIT, close_pipes, NULL);
361 assert(!ret);
362
363 fd_tracker_destroy(tracker);
364 ret = rmdir(test_directory);
365 ok(ret == 0, "Test directory is empty");
366 free(test_directory);
367 free(unlinked_files_directory);
368 }
369
370 /*
371 * Validate that the tracker refuses to track two identical unsuspendable
372 * file descriptors.
373 */
374 static
375 void test_unsuspendable_close_untracked(void)
376 {
377 int ret, stdout_fd = fileno(stdout), unknown_fds[2], out_fd;
378 struct fd_tracker *tracker;
379 char *test_directory = NULL, *unlinked_files_directory = NULL;
380
381 get_temporary_directories(&test_directory, &unlinked_files_directory);
382
383 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
384 if (!tracker) {
385 goto end;;
386 }
387
388 ret = pipe(unknown_fds);
389 assert(!ret);
390 ret = close(unknown_fds[0]);
391 assert(ret == 0);
392 ret = close(unknown_fds[1]);
393 assert(ret == 0);
394
395 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
396 NULL, 1, noop_open, &stdout_fd);
397 assert(!ret);
398
399 ret = fd_tracker_close_unsuspendable_fd(tracker,
400 unknown_fds, 1, noop_close, NULL);
401 ok(ret == -EINVAL, "EINVAL reported on close of an untracked file descriptor");
402
403 ret = fd_tracker_close_unsuspendable_fd(tracker,
404 &stdout_fd, 1, noop_close, NULL);
405 assert(!ret);
406
407 fd_tracker_destroy(tracker);
408 ret = rmdir(test_directory);
409 ok(ret == 0, "Test directory is empty");
410 end:
411 free(test_directory);
412 free(unlinked_files_directory);
413 }
414
415 static int open_files(struct fd_tracker *tracker,
416 struct lttng_directory_handle *directory,
417 unsigned int count,
418 struct fs_handle **handles,
419 char **file_paths)
420 {
421 int ret = 0;
422 unsigned int i;
423
424 for (i = 0; i < count; i++) {
425 int p_ret;
426 char *file_path;
427 struct fs_handle *handle;
428 mode_t mode = S_IWUSR | S_IRUSR;
429
430 p_ret = asprintf(&file_path, "file-%u", i);
431 assert(p_ret >= 0);
432 file_paths[i] = file_path;
433
434 handle = fd_tracker_open_fs_handle(tracker, directory, file_path,
435 O_RDWR | O_CREAT, &mode);
436 if (!handle) {
437 ret = -1;
438 break;
439 }
440 handles[i] = handle;
441 }
442 return ret;
443 }
444
445 static int open_same_file(struct fd_tracker *tracker,
446 struct lttng_directory_handle *directory,
447 const char *file,
448 unsigned int count,
449 struct fs_handle **handles)
450 {
451 int ret = 0;
452 unsigned int i;
453
454 for (i = 0; i < count; i++) {
455 struct fs_handle *handle;
456 mode_t mode = S_IWUSR | S_IRUSR;
457
458 handle = fd_tracker_open_fs_handle(tracker, directory, file,
459 O_RDWR | O_CREAT, &mode);
460 if (!handle) {
461 ret = -1;
462 break;
463 }
464 handles[i] = handle;
465 }
466 return ret;
467 }
468
469 static
470 int cleanup_files(struct fd_tracker *tracker, const char *dir,
471 unsigned int count, struct fs_handle **handles,
472 char **file_paths)
473 {
474 int ret = 0;
475 unsigned int i;
476
477 for (i = 0; i < count; i++) {
478 char *file_path = file_paths[i];
479
480 if (!file_path) {
481 break;
482 }
483 if (fs_handle_unlink(handles[i])) {
484 diag("Failed to unlink fs_handle to file %s", file_path);
485 ret = -1;
486 }
487 if (fs_handle_close(handles[i])) {
488 diag("Failed to close fs_handle to file %s", file_path);
489 ret = -1;
490 }
491 free(file_path);
492 }
493 return ret;
494 }
495
496 static
497 void test_suspendable_limit(void)
498 {
499 int ret;
500 const int files_to_create = TRACKER_FD_LIMIT * 10;
501 struct fd_tracker *tracker;
502 char *test_directory = NULL, *unlinked_files_directory = NULL;
503 char *output_files[files_to_create];
504 struct fs_handle *handles[files_to_create];
505 struct lttng_directory_handle *dir_handle = NULL;
506 int dir_handle_fd_count;
507
508 memset(output_files, 0, sizeof(output_files));
509 memset(handles, 0, sizeof(handles));
510
511 get_temporary_directories(&test_directory, &unlinked_files_directory);
512
513 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
514 if (!tracker) {
515 goto end;
516 }
517
518 dir_handle = lttng_directory_handle_create(test_directory);
519 assert(dir_handle);
520 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
521
522 ret = open_files(tracker, dir_handle, files_to_create, handles,
523 output_files);
524 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
525 files_to_create, TRACKER_FD_LIMIT);
526 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
527 dir_handle_fd_count);
528
529 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
530 output_files);
531 ok(!ret, "Close all opened filesystem handles");
532 ret = rmdir(test_directory);
533 ok(ret == 0, "Test directory is empty");
534 fd_tracker_destroy(tracker);
535 lttng_directory_handle_put(dir_handle);
536 end:
537 free(test_directory);
538 free(unlinked_files_directory);
539 }
540
541 static
542 void test_mixed_limit(void)
543 {
544 int ret;
545 const int files_to_create = TRACKER_FD_LIMIT;
546 struct fd_tracker *tracker;
547 char *test_directory = NULL, *unlinked_files_directory = NULL;
548 char *output_files[files_to_create];
549 struct fs_handle *handles[files_to_create];
550 struct lttng_directory_handle *dir_handle = NULL;
551 int dir_handle_fd_count;
552
553 memset(output_files, 0, sizeof(output_files));
554 memset(handles, 0, sizeof(handles));
555
556 get_temporary_directories(&test_directory, &unlinked_files_directory);
557
558 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
559 if (!tracker) {
560 goto end;
561 }
562
563 dir_handle = lttng_directory_handle_create(test_directory);
564 assert(dir_handle);
565 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
566
567 ret = open_files(tracker, dir_handle, files_to_create, handles,
568 output_files);
569 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
570 files_to_create, TRACKER_FD_LIMIT);
571 diag("Check file descriptor count after opening %u files", files_to_create);
572 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
573 dir_handle_fd_count);
574
575 /*
576 * Open unsuspendable fds (stdin, stdout, stderr) and verify that the fd
577 * cap is still respected.
578 */
579 diag("Check file descriptor count after adding %d unsuspendable fds",
580 STDIO_FD_COUNT);
581 track_std_fds(tracker);
582 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
583 dir_handle_fd_count);
584 diag("Untrack unsuspendable file descriptors");
585 untrack_std_fds(tracker);
586 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
587 dir_handle_fd_count);
588
589 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
590 output_files);
591 ok(!ret, "Close all opened filesystem handles");
592 ret = rmdir(test_directory);
593 ok(ret == 0, "Test directory is empty");
594 fd_tracker_destroy(tracker);
595 lttng_directory_handle_put(dir_handle);
596 end:
597 free(test_directory);
598 free(unlinked_files_directory);
599 }
600
601 /*
602 * Open more files than allowed by the fd tracker's cap and write,
603 * byte-by-byte, and in round-robin, a string. The goal is to force
604 * the fd tracker to suspend and resume the fs_handles often and
605 * verify that the fd cap is always respected.
606 *
607 * The content of the files is also verified at the end.
608 */
609 static
610 void test_suspendable_restore(void)
611 {
612 int ret;
613 const int files_to_create = TRACKER_FD_LIMIT * 10;
614 struct fd_tracker *tracker;
615 char *output_files[files_to_create];
616 struct fs_handle *handles[files_to_create];
617 size_t content_index;
618 int handle_index;
619 bool write_success = true;
620 bool fd_cap_respected = true;
621 bool content_ok = true;
622 struct lttng_directory_handle *dir_handle = NULL;
623 int dir_handle_fd_count;
624 char *test_directory = NULL, *unlinked_files_directory = NULL;
625
626 memset(output_files, 0, sizeof(output_files));
627 memset(handles, 0, sizeof(handles));
628
629 get_temporary_directories(&test_directory, &unlinked_files_directory);
630
631 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
632 if (!tracker) {
633 goto end;
634 }
635
636 dir_handle = lttng_directory_handle_create(test_directory);
637 assert(dir_handle);
638 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
639
640 ret = open_files(tracker, dir_handle, files_to_create, handles,
641 output_files);
642 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
643 files_to_create, TRACKER_FD_LIMIT);
644 diag("Check file descriptor count after opening %u files", files_to_create);
645 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
646 dir_handle_fd_count);
647
648 for (content_index = 0; content_index < sizeof(file_contents); content_index++) {
649 for (handle_index = 0; handle_index < files_to_create; handle_index++) {
650 int fd;
651 struct fs_handle *handle = handles[handle_index];
652 const char *path = output_files[handle_index];
653
654 fd = fs_handle_get_fd(handle);
655 if (fd < 0) {
656 write_success = false;
657 diag("Failed to restore fs_handle to %s",
658 path);
659 goto skip_write;
660 }
661
662 do {
663 ret = write(fd, file_contents + content_index, 1);
664 } while (ret < 0 && errno == EINTR);
665
666 if (ret != 1) {
667 write_success = false;
668 PERROR("write() to %s failed", path);
669 goto skip_write;
670 }
671
672 if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT +
673 unknown_fds_count +
674 dir_handle_fd_count)) {
675 fd_cap_respected = false;
676 }
677
678 fs_handle_put_fd(handle);
679 }
680 }
681 skip_write:
682 ok(write_success, "Wrote reference string to %d files",
683 files_to_create);
684 ok(fd_cap_respected, "FD tracker enforced the file descriptor cap");
685
686 /* Validate the contents of the files. */
687 for (handle_index = 0; handle_index < files_to_create; handle_index++) {
688 struct stat fd_stat;
689 const char *path = output_files[handle_index];
690 char read_buf[sizeof(file_contents)];
691 char *read_pos;
692 size_t to_read = sizeof(read_buf);
693 int fd;
694
695 fd = lttng_directory_handle_open_file(
696 dir_handle, path, O_RDONLY, 0);
697 assert(fd >= 0);
698 ret = fstat(fd, &fd_stat);
699 assert(!ret);
700 if (fd_stat.st_size != sizeof(file_contents)) {
701 diag("Content size of file %s doesn't match, got %" PRId64 ", expected %zu",
702 path, (int64_t) fd_stat.st_size,
703 sizeof(file_contents));
704 content_ok = false;
705 (void) close(fd);
706 break;
707 }
708
709 read_pos = read_buf;
710 do {
711 ret = read(fd, read_pos, to_read);
712 if (ret > 0) {
713 to_read -= ret;
714 read_pos += ret;
715 }
716 } while (to_read && (ret < 0 && errno == EINTR));
717 if (ret < 0) {
718 content_ok = false;
719 PERROR("Failed to read file %s", path);
720 (void) close(fd);
721 break;
722 }
723
724 if (strcmp(file_contents, read_buf)) {
725 content_ok = false;
726 diag("File content doesn't match the expectated string");
727 (void) close(fd);
728 break;
729 }
730 (void) close(fd);
731 }
732 ok(content_ok, "Files contain the expected content");
733 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
734 output_files);
735 ok(!ret, "Close all opened filesystem handles");
736 ret = rmdir(test_directory);
737 ok(ret == 0, "Test directory is empty");
738 fd_tracker_destroy(tracker);
739 lttng_directory_handle_put(dir_handle);
740 end:
741 free(test_directory);
742 free(unlinked_files_directory);
743 }
744
745 static
746 void test_unlink(void)
747 {
748 int ret;
749 struct fd_tracker *tracker;
750 const int handles_to_open = 2;
751 struct fs_handle *handles[handles_to_open];
752 struct fs_handle *new_handle = NULL;
753 struct stat statbuf;
754 struct lttng_directory_handle *dir_handle = NULL;
755 const char file_name[] = "my_file";
756 char *test_directory = NULL, *unlinked_files_directory = NULL;
757 char *unlinked_file_zero = NULL, *unlinked_file_one = NULL;
758 int fd;
759
760 get_temporary_directories(&test_directory, &unlinked_files_directory);
761 ret = asprintf(&unlinked_file_zero, "%s/%u", unlinked_files_directory,
762 0);
763 assert(ret > 0);
764 ret = asprintf(&unlinked_file_one, "%s/%u", unlinked_files_directory,
765 1);
766 assert(ret > 0);
767
768 tracker = fd_tracker_create(unlinked_files_directory, 1);
769 if (!tracker) {
770 goto end;
771 }
772
773 dir_handle = lttng_directory_handle_create(test_directory);
774 assert(dir_handle);
775
776 /* Open two handles to the same file. */
777 ret = open_same_file(tracker, dir_handle, file_name, handles_to_open,
778 handles);
779 ok(!ret, "Successfully opened %i handles to %s/%s", handles_to_open,
780 test_directory, file_name);
781 if (ret) {
782 goto end;
783 }
784
785 /*
786 * Unlinking the first handle should cause the file to be renamed
787 * to '0'.
788 */
789 ret = fs_handle_unlink(handles[0]);
790 ok(!ret, "Successfully unlinked the first handle to %s/%s",
791 test_directory, file_name);
792
793 /*
794 * The original file should no longer exist on the file system, and a
795 * new file named '0' should exist.
796 */
797 ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
798 errno == ENOENT,
799 "%s no longer present on file system after unlink",
800 file_name);
801 ok(lttng_directory_handle_stat(
802 dir_handle, unlinked_file_zero, &statbuf) == 0,
803 "%s exists on file system after unlink",
804 unlinked_file_zero);
805
806 /*
807 * It should be possible to use the file descriptors of both handles.
808 * Since only one file descriptor can be opened at once, this should
809 * force the fd_tracker to suspend and restore the handles.
810 */
811 fd = fs_handle_get_fd(handles[0]);
812 ok(fd >= 0, "Got fd from first handle");
813
814 fd = fs_handle_get_fd(handles[1]);
815 ok (fd < 0, "fd tracker does not allow two fds to be used at once");
816
817 fs_handle_put_fd(handles[0]);
818 fd = fs_handle_get_fd(handles[1]);
819 ok(fd >= 0, "Got fd from second handle");
820 fs_handle_put_fd(handles[1]);
821
822 /* The second unlink should fail with -ENOENT. */
823 ret = fs_handle_unlink(handles[1]);
824 ok(ret == -ENOENT,
825 "ENOENT is reported when attempting to unlink the second handle to %s/%s",
826 test_directory, file_name);
827
828 /*
829 * Opening a new handle to 'my_file' should succeed.
830 */
831 ret = open_same_file(tracker, dir_handle, file_name, 1, &new_handle);
832 ok(!ret, "Successfully opened a new handle to previously unlinked file %s/%s",
833 test_directory, file_name);
834 assert(new_handle);
835
836 /*
837 * Unlinking the new handle should cause the file to be renamed
838 * to '1' since '0' already exists.
839 */
840 ret = fs_handle_unlink(new_handle);
841 ok(!ret, "Successfully unlinked the new handle handle to %s/%s",
842 test_directory, file_name);
843 ok(stat(unlinked_file_one, &statbuf) == 0,
844 "%s exists on file system after unlink",
845 unlinked_file_one);
846
847 ret = fs_handle_close(handles[0]);
848 ok(!ret, "Successfully closed the first handle");
849 ret = fs_handle_close(handles[1]);
850 ok(!ret, "Successfully closed the second handle");
851 ret = fs_handle_close(new_handle);
852 ok(!ret, "Successfully closed the third handle");
853
854 ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
855 errno == ENOENT,
856 "%s no longer present on file system after handle close",
857 file_name);
858 ok(lttng_directory_handle_stat(
859 dir_handle, unlinked_file_zero, &statbuf) == -1 &&
860 errno == ENOENT,
861 "%s no longer present on file system after handle close",
862 unlinked_file_zero);
863 ok(lttng_directory_handle_stat(dir_handle, unlinked_file_one,
864 &statbuf) == -1 &&
865 errno == ENOENT,
866 "%s no longer present on file system after handle close",
867 unlinked_file_one);
868
869 ret = rmdir(test_directory);
870 ok(ret == 0, "Test directory is empty");
871 end:
872 fd_tracker_destroy(tracker);
873 free(test_directory);
874 free(unlinked_files_directory);
875 free(unlinked_file_zero);
876 free(unlinked_file_one);
877 lttng_directory_handle_put(dir_handle);
878 }
879
880 int main(int argc, char **argv)
881 {
882 plan_tests(NUM_TESTS);
883 diag("File descriptor tracker unit tests");
884
885 rcu_register_thread();
886
887 unknown_fds_count = fd_count() - STDIO_FD_COUNT;
888 assert(unknown_fds_count >= 0);
889
890 diag("Unsuspendable - basic");
891 test_unsuspendable_basic();
892 diag("Unsuspendable - callback return values");
893 test_unsuspendable_cb_return();
894 diag("Unsuspendable - duplicate file descriptors");
895 test_unsuspendable_duplicate();
896 diag("Unsuspendable - closing an untracked file descriptor");
897 test_unsuspendable_close_untracked();
898 diag("Unsuspendable - check that file descriptor limit is enforced");
899 test_unsuspendable_limit();
900
901 diag("Suspendable - check that file descriptor limit is enforced");
902 test_suspendable_limit();
903 diag("Suspendable - restoration test");
904 test_suspendable_restore();
905
906 diag("Mixed - check that file descriptor limit is enforced");
907 test_mixed_limit();
908
909 diag("Suspendable - Unlinking test");
910 test_unlink();
911
912 rcu_barrier();
913 rcu_unregister_thread();
914 return exit_status();
915 }
This page took 0.083652 seconds and 5 git commands to generate.