7c818b94bd2fb6cee2749016f89fc902f4278cce
[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 assert(close(unknown_fds[0]) == 0);
391 assert(close(unknown_fds[1]) == 0);
392
393 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
394 NULL, 1, noop_open, &stdout_fd);
395 assert(!ret);
396
397 ret = fd_tracker_close_unsuspendable_fd(tracker,
398 unknown_fds, 1, noop_close, NULL);
399 ok(ret == -EINVAL, "EINVAL reported on close of an untracked file descriptor");
400
401 ret = fd_tracker_close_unsuspendable_fd(tracker,
402 &stdout_fd, 1, noop_close, NULL);
403 assert(!ret);
404
405 fd_tracker_destroy(tracker);
406 ret = rmdir(test_directory);
407 ok(ret == 0, "Test directory is empty");
408 end:
409 free(test_directory);
410 free(unlinked_files_directory);
411 }
412
413 static int open_files(struct fd_tracker *tracker,
414 struct lttng_directory_handle *directory,
415 unsigned int count,
416 struct fs_handle **handles,
417 char **file_paths)
418 {
419 int ret = 0;
420 unsigned int i;
421
422 for (i = 0; i < count; i++) {
423 int p_ret;
424 char *file_path;
425 struct fs_handle *handle;
426 mode_t mode = S_IWUSR | S_IRUSR;
427
428 p_ret = asprintf(&file_path, "file-%u", i);
429 assert(p_ret >= 0);
430 file_paths[i] = file_path;
431
432 handle = fd_tracker_open_fs_handle(tracker, directory, file_path,
433 O_RDWR | O_CREAT, &mode);
434 if (!handle) {
435 ret = -1;
436 break;
437 }
438 handles[i] = handle;
439 }
440 return ret;
441 }
442
443 static int open_same_file(struct fd_tracker *tracker,
444 struct lttng_directory_handle *directory,
445 const char *file,
446 unsigned int count,
447 struct fs_handle **handles)
448 {
449 int ret = 0;
450 unsigned int i;
451
452 for (i = 0; i < count; i++) {
453 struct fs_handle *handle;
454 mode_t mode = S_IWUSR | S_IRUSR;
455
456 handle = fd_tracker_open_fs_handle(tracker, directory, file,
457 O_RDWR | O_CREAT, &mode);
458 if (!handle) {
459 ret = -1;
460 break;
461 }
462 handles[i] = handle;
463 }
464 return ret;
465 }
466
467 static
468 int cleanup_files(struct fd_tracker *tracker, const char *dir,
469 unsigned int count, struct fs_handle **handles,
470 char **file_paths)
471 {
472 int ret = 0;
473 unsigned int i;
474
475 for (i = 0; i < count; i++) {
476 char *file_path = file_paths[i];
477
478 if (!file_path) {
479 break;
480 }
481 if (fs_handle_unlink(handles[i])) {
482 diag("Failed to unlink fs_handle to file %s", file_path);
483 ret = -1;
484 }
485 if (fs_handle_close(handles[i])) {
486 diag("Failed to close fs_handle to file %s", file_path);
487 ret = -1;
488 }
489 free(file_path);
490 }
491 return ret;
492 }
493
494 static
495 void test_suspendable_limit(void)
496 {
497 int ret;
498 const int files_to_create = TRACKER_FD_LIMIT * 10;
499 struct fd_tracker *tracker;
500 char *test_directory = NULL, *unlinked_files_directory = NULL;
501 char *output_files[files_to_create];
502 struct fs_handle *handles[files_to_create];
503 struct lttng_directory_handle *dir_handle = NULL;
504 int dir_handle_fd_count;
505
506 memset(output_files, 0, sizeof(output_files));
507 memset(handles, 0, sizeof(handles));
508
509 get_temporary_directories(&test_directory, &unlinked_files_directory);
510
511 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
512 if (!tracker) {
513 goto end;
514 }
515
516 dir_handle = lttng_directory_handle_create(test_directory);
517 assert(dir_handle);
518 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
519
520 ret = open_files(tracker, dir_handle, files_to_create, handles,
521 output_files);
522 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
523 files_to_create, TRACKER_FD_LIMIT);
524 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
525 dir_handle_fd_count);
526
527 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
528 output_files);
529 ok(!ret, "Close all opened filesystem handles");
530 ret = rmdir(test_directory);
531 ok(ret == 0, "Test directory is empty");
532 fd_tracker_destroy(tracker);
533 lttng_directory_handle_put(dir_handle);
534 end:
535 free(test_directory);
536 free(unlinked_files_directory);
537 }
538
539 static
540 void test_mixed_limit(void)
541 {
542 int ret;
543 const int files_to_create = TRACKER_FD_LIMIT;
544 struct fd_tracker *tracker;
545 char *test_directory = NULL, *unlinked_files_directory = NULL;
546 char *output_files[files_to_create];
547 struct fs_handle *handles[files_to_create];
548 struct lttng_directory_handle *dir_handle = NULL;
549 int dir_handle_fd_count;
550
551 memset(output_files, 0, sizeof(output_files));
552 memset(handles, 0, sizeof(handles));
553
554 get_temporary_directories(&test_directory, &unlinked_files_directory);
555
556 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
557 if (!tracker) {
558 goto end;
559 }
560
561 dir_handle = lttng_directory_handle_create(test_directory);
562 assert(dir_handle);
563 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
564
565 ret = open_files(tracker, dir_handle, files_to_create, handles,
566 output_files);
567 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
568 files_to_create, TRACKER_FD_LIMIT);
569 diag("Check file descriptor count after opening %u files", files_to_create);
570 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
571 dir_handle_fd_count);
572
573 /*
574 * Open unsuspendable fds (stdin, stdout, stderr) and verify that the fd
575 * cap is still respected.
576 */
577 diag("Check file descriptor count after adding %d unsuspendable fds",
578 STDIO_FD_COUNT);
579 track_std_fds(tracker);
580 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
581 dir_handle_fd_count);
582 diag("Untrack unsuspendable file descriptors");
583 untrack_std_fds(tracker);
584 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
585 dir_handle_fd_count);
586
587 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
588 output_files);
589 ok(!ret, "Close all opened filesystem handles");
590 ret = rmdir(test_directory);
591 ok(ret == 0, "Test directory is empty");
592 fd_tracker_destroy(tracker);
593 lttng_directory_handle_put(dir_handle);
594 end:
595 free(test_directory);
596 free(unlinked_files_directory);
597 }
598
599 /*
600 * Open more files than allowed by the fd tracker's cap and write,
601 * byte-by-byte, and in round-robin, a string. The goal is to force
602 * the fd tracker to suspend and resume the fs_handles often and
603 * verify that the fd cap is always respected.
604 *
605 * The content of the files is also verified at the end.
606 */
607 static
608 void test_suspendable_restore(void)
609 {
610 int ret;
611 const int files_to_create = TRACKER_FD_LIMIT * 10;
612 struct fd_tracker *tracker;
613 char *output_files[files_to_create];
614 struct fs_handle *handles[files_to_create];
615 size_t content_index;
616 int handle_index;
617 bool write_success = true;
618 bool fd_cap_respected = true;
619 bool content_ok = true;
620 struct lttng_directory_handle *dir_handle = NULL;
621 int dir_handle_fd_count;
622 char *test_directory = NULL, *unlinked_files_directory = NULL;
623
624 memset(output_files, 0, sizeof(output_files));
625 memset(handles, 0, sizeof(handles));
626
627 get_temporary_directories(&test_directory, &unlinked_files_directory);
628
629 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
630 if (!tracker) {
631 goto end;
632 }
633
634 dir_handle = lttng_directory_handle_create(test_directory);
635 assert(dir_handle);
636 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
637
638 ret = open_files(tracker, dir_handle, files_to_create, handles,
639 output_files);
640 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
641 files_to_create, TRACKER_FD_LIMIT);
642 diag("Check file descriptor count after opening %u files", files_to_create);
643 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
644 dir_handle_fd_count);
645
646 for (content_index = 0; content_index < sizeof(file_contents); content_index++) {
647 for (handle_index = 0; handle_index < files_to_create; handle_index++) {
648 int fd;
649 struct fs_handle *handle = handles[handle_index];
650 const char *path = output_files[handle_index];
651
652 fd = fs_handle_get_fd(handle);
653 if (fd < 0) {
654 write_success = false;
655 diag("Failed to restore fs_handle to %s",
656 path);
657 goto skip_write;
658 }
659
660 do {
661 ret = write(fd, file_contents + content_index, 1);
662 } while (ret < 0 && errno == EINTR);
663
664 if (ret != 1) {
665 write_success = false;
666 PERROR("write() to %s failed", path);
667 goto skip_write;
668 }
669
670 if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT +
671 unknown_fds_count +
672 dir_handle_fd_count)) {
673 fd_cap_respected = false;
674 }
675
676 fs_handle_put_fd(handle);
677 }
678 }
679 skip_write:
680 ok(write_success, "Wrote reference string to %d files",
681 files_to_create);
682 ok(fd_cap_respected, "FD tracker enforced the file descriptor cap");
683
684 /* Validate the contents of the files. */
685 for (handle_index = 0; handle_index < files_to_create; handle_index++) {
686 struct stat fd_stat;
687 const char *path = output_files[handle_index];
688 char read_buf[sizeof(file_contents)];
689 char *read_pos;
690 size_t to_read = sizeof(read_buf);
691 int fd;
692
693 fd = lttng_directory_handle_open_file(
694 dir_handle, path, O_RDONLY, 0);
695 assert(fd >= 0);
696 ret = fstat(fd, &fd_stat);
697 assert(!ret);
698 if (fd_stat.st_size != sizeof(file_contents)) {
699 diag("Content size of file %s doesn't match, got %" PRId64 ", expected %zu",
700 path, (int64_t) fd_stat.st_size,
701 sizeof(file_contents));
702 content_ok = false;
703 (void) close(fd);
704 break;
705 }
706
707 read_pos = read_buf;
708 do {
709 ret = read(fd, read_pos, to_read);
710 if (ret > 0) {
711 to_read -= ret;
712 read_pos += ret;
713 }
714 } while (to_read && (ret < 0 && errno == EINTR));
715 if (ret < 0) {
716 content_ok = false;
717 PERROR("Failed to read file %s", path);
718 (void) close(fd);
719 break;
720 }
721
722 if (strcmp(file_contents, read_buf)) {
723 content_ok = false;
724 diag("File content doesn't match the expectated string");
725 (void) close(fd);
726 break;
727 }
728 (void) close(fd);
729 }
730 ok(content_ok, "Files contain the expected content");
731 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
732 output_files);
733 ok(!ret, "Close all opened filesystem handles");
734 ret = rmdir(test_directory);
735 ok(ret == 0, "Test directory is empty");
736 fd_tracker_destroy(tracker);
737 lttng_directory_handle_put(dir_handle);
738 end:
739 free(test_directory);
740 free(unlinked_files_directory);
741 }
742
743 static
744 void test_unlink(void)
745 {
746 int ret;
747 struct fd_tracker *tracker;
748 const int handles_to_open = 2;
749 struct fs_handle *handles[handles_to_open];
750 struct fs_handle *new_handle = NULL;
751 struct stat statbuf;
752 struct lttng_directory_handle *dir_handle = NULL;
753 const char file_name[] = "my_file";
754 char *test_directory = NULL, *unlinked_files_directory = NULL;
755 char *unlinked_file_zero = NULL, *unlinked_file_one = NULL;
756 int fd;
757
758 get_temporary_directories(&test_directory, &unlinked_files_directory);
759 ret = asprintf(&unlinked_file_zero, "%s/%u", unlinked_files_directory,
760 0);
761 assert(ret > 0);
762 ret = asprintf(&unlinked_file_one, "%s/%u", unlinked_files_directory,
763 1);
764 assert(ret > 0);
765
766 tracker = fd_tracker_create(unlinked_files_directory, 1);
767 if (!tracker) {
768 goto end;
769 }
770
771 dir_handle = lttng_directory_handle_create(test_directory);
772 assert(dir_handle);
773
774 /* Open two handles to the same file. */
775 ret = open_same_file(tracker, dir_handle, file_name, handles_to_open,
776 handles);
777 ok(!ret, "Successfully opened %i handles to %s/%s", handles_to_open,
778 test_directory, file_name);
779 if (ret) {
780 goto end;
781 }
782
783 /*
784 * Unlinking the first handle should cause the file to be renamed
785 * to '0'.
786 */
787 ret = fs_handle_unlink(handles[0]);
788 ok(!ret, "Successfully unlinked the first handle to %s/%s",
789 test_directory, file_name);
790
791 /*
792 * The original file should no longer exist on the file system, and a
793 * new file named '0' should exist.
794 */
795 ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
796 errno == ENOENT,
797 "%s no longer present on file system after unlink",
798 file_name);
799 ok(lttng_directory_handle_stat(
800 dir_handle, unlinked_file_zero, &statbuf) == 0,
801 "%s exists on file system after unlink",
802 unlinked_file_zero);
803
804 /*
805 * It should be possible to use the file descriptors of both handles.
806 * Since only one file descriptor can be opened at once, this should
807 * force the fd_tracker to suspend and restore the handles.
808 */
809 fd = fs_handle_get_fd(handles[0]);
810 ok(fd >= 0, "Got fd from first handle");
811
812 fd = fs_handle_get_fd(handles[1]);
813 ok (fd < 0, "fd tracker does not allow two fds to be used at once");
814
815 fs_handle_put_fd(handles[0]);
816 fd = fs_handle_get_fd(handles[1]);
817 ok(fd >= 0, "Got fd from second handle");
818 fs_handle_put_fd(handles[1]);
819
820 /* The second unlink should fail with -ENOENT. */
821 ret = fs_handle_unlink(handles[1]);
822 ok(ret == -ENOENT,
823 "ENOENT is reported when attempting to unlink the second handle to %s/%s",
824 test_directory, file_name);
825
826 /*
827 * Opening a new handle to 'my_file' should succeed.
828 */
829 ret = open_same_file(tracker, dir_handle, file_name, 1, &new_handle);
830 ok(!ret, "Successfully opened a new handle to previously unlinked file %s/%s",
831 test_directory, file_name);
832 assert(new_handle);
833
834 /*
835 * Unlinking the new handle should cause the file to be renamed
836 * to '1' since '0' already exists.
837 */
838 ret = fs_handle_unlink(new_handle);
839 ok(!ret, "Successfully unlinked the new handle handle to %s/%s",
840 test_directory, file_name);
841 ok(stat(unlinked_file_one, &statbuf) == 0,
842 "%s exists on file system after unlink",
843 unlinked_file_one);
844
845 ret = fs_handle_close(handles[0]);
846 ok(!ret, "Successfully closed the first handle");
847 ret = fs_handle_close(handles[1]);
848 ok(!ret, "Successfully closed the second handle");
849 ret = fs_handle_close(new_handle);
850 ok(!ret, "Successfully closed the third handle");
851
852 ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
853 errno == ENOENT,
854 "%s no longer present on file system after handle close",
855 file_name);
856 ok(lttng_directory_handle_stat(
857 dir_handle, unlinked_file_zero, &statbuf) == -1 &&
858 errno == ENOENT,
859 "%s no longer present on file system after handle close",
860 unlinked_file_zero);
861 ok(lttng_directory_handle_stat(dir_handle, unlinked_file_one,
862 &statbuf) == -1 &&
863 errno == ENOENT,
864 "%s no longer present on file system after handle close",
865 unlinked_file_one);
866
867 ret = rmdir(test_directory);
868 ok(ret == 0, "Test directory is empty");
869 end:
870 fd_tracker_destroy(tracker);
871 free(test_directory);
872 free(unlinked_files_directory);
873 free(unlinked_file_zero);
874 free(unlinked_file_one);
875 lttng_directory_handle_put(dir_handle);
876 }
877
878 int main(int argc, char **argv)
879 {
880 plan_tests(NUM_TESTS);
881 diag("File descriptor tracker unit tests");
882
883 rcu_register_thread();
884
885 unknown_fds_count = fd_count() - STDIO_FD_COUNT;
886 assert(unknown_fds_count >= 0);
887
888 diag("Unsuspendable - basic");
889 test_unsuspendable_basic();
890 diag("Unsuspendable - callback return values");
891 test_unsuspendable_cb_return();
892 diag("Unsuspendable - duplicate file descriptors");
893 test_unsuspendable_duplicate();
894 diag("Unsuspendable - closing an untracked file descriptor");
895 test_unsuspendable_close_untracked();
896 diag("Unsuspendable - check that file descriptor limit is enforced");
897 test_unsuspendable_limit();
898
899 diag("Suspendable - check that file descriptor limit is enforced");
900 test_suspendable_limit();
901 diag("Suspendable - restoration test");
902 test_suspendable_restore();
903
904 diag("Mixed - check that file descriptor limit is enforced");
905 test_mixed_limit();
906
907 diag("Suspendable - Unlinking test");
908 test_unlink();
909
910 rcu_barrier();
911 rcu_unregister_thread();
912 return exit_status();
913 }
This page took 0.046788 seconds and 4 git commands to generate.