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