Typo: 'Descritptor' -> 'Descriptor'
[lttng-tools.git] / tests / unit / test_fd_tracker.c
CommitLineData
9ca3e8a2 1/*
9d16b343 2 * Copyright (C) 2018 Jérémie Galarneau <jeremie.galarneau@efficios.com>
9ca3e8a2 3 *
9d16b343 4 * SPDX-License-Identifier: GPL-2.0-only
9ca3e8a2 5 *
9ca3e8a2
JG
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>
f0faf175 22#include <sys/types.h>
9ca3e8a2
JG
23
24#include <urcu.h>
25
f7c3ffd7 26#include <common/compat/directory-handle.h>
9ca3e8a2 27#include <common/error.h>
f7c3ffd7 28#include <common/fd-tracker/fd-tracker.h>
9ca3e8a2
JG
29
30/* For error.h */
31int lttng_opt_quiet = 1;
32int lttng_opt_verbose;
33int lttng_opt_mi;
34
35/* Number of TAP tests in this file */
f7c3ffd7 36#define NUM_TESTS 61
9ca3e8a2
JG
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"
f7c3ffd7 41#define TEST_UNLINK_DIRECTORY_NAME "unlinked_files"
9ca3e8a2
JG
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 */
49int unknown_fds_count;
50
51const 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
f8e06d39 57static
f7c3ffd7
JG
58void 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
3ea695b4 80static
9ca3e8a2
JG
81int 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--;
e0efb2c8
JG
102 if (closedir(dir)) {
103 perror("# Failed to close test program's self/fd directory file descriptor");
104 }
9ca3e8a2
JG
105end:
106 return count;
107}
108
109static
110void 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
119static
120int noop_open(void *data, int *fds)
121{
122 *fds = *((int *) data);
123 return 0;
124}
125
126static
127int noop_close(void *data, int *fds)
128{
129 return 0;
130}
131
132static
133void 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
154static
155void 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 */
179static
180void test_unsuspendable_basic(void)
181{
f7c3ffd7 182 int ret;
9ca3e8a2 183 struct fd_tracker *tracker;
f7c3ffd7
JG
184 char *test_directory = NULL, *unlinked_files_directory = NULL;
185
186 get_temporary_directories(&test_directory, &unlinked_files_directory);
9ca3e8a2 187
f7c3ffd7 188 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
189 ok(tracker, "Created an fd tracker with a limit of %d simulateously opened file descriptors",
190 TRACKER_FD_LIMIT);
191 if (!tracker) {
f6bef966 192 goto end;
9ca3e8a2
JG
193 }
194
195 track_std_fds(tracker);
196 untrack_std_fds(tracker);
197
198 fd_tracker_destroy(tracker);
f7c3ffd7
JG
199 ret = rmdir(test_directory);
200 ok(ret == 0, "Test directory is empty");
f6bef966 201end:
f7c3ffd7
JG
202 free(test_directory);
203 free(unlinked_files_directory);
9ca3e8a2
JG
204}
205
206static
207int error_open(void *data, int *fds)
208{
209 return *((int *) data);
210}
211
212static
213int 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 */
222static
223void 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;
f7c3ffd7 228 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2 229
f7c3ffd7
JG
230 get_temporary_directories(&test_directory, &unlinked_files_directory);
231
232 tracker = fd_tracker_create(test_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
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);
f7c3ffd7
JG
258 ret = rmdir(test_directory);
259 ok(ret == 0, "Test directory is empty");
260 free(test_directory);
261 free(unlinked_files_directory);
9ca3e8a2
JG
262}
263
264/*
265 * Validate that the tracker refuses to track two identical unsuspendable
266 * file descriptors.
267 */
268static
269void test_unsuspendable_duplicate(void)
270{
271 int ret, stdout_fd = fileno(stdout), out_fd;
272 struct fd_tracker *tracker;
f7c3ffd7
JG
273 char *test_directory = NULL, *unlinked_files_directory = NULL;
274
275 get_temporary_directories(&test_directory, &unlinked_files_directory);
9ca3e8a2 276
f7c3ffd7 277 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
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);
f7c3ffd7
JG
292 ret = rmdir(test_directory);
293 ok(ret == 0, "Test directory is empty");
294 free(test_directory);
295 free(unlinked_files_directory);
9ca3e8a2
JG
296}
297
298static
299int 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
314static
315int 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
ea05fafd 332 * when unsuspendable file descriptors are being opened.
9ca3e8a2
JG
333 */
334static
335void 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];
f7c3ffd7
JG
340 char *test_directory = NULL, *unlinked_files_directory = NULL;
341
342 get_temporary_directories(&test_directory, &unlinked_files_directory);
9ca3e8a2
JG
343
344 /* This test assumes TRACKER_FD_LIMIT is a multiple of 2. */
345 assert((TRACKER_FD_LIMIT % 2 == 0) && TRACKER_FD_LIMIT);
346
f7c3ffd7 347 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
348 assert(tracker);
349
350 ret = fd_tracker_open_unsuspendable_fd(tracker, fds,
351 NULL, TRACKER_FD_LIMIT, open_pipes, NULL);
ea05fafd 352 ok(ret == 0, "File descriptor tracker allowed the user to meet its limit with unsuspendable file descriptors (%d)",
9ca3e8a2
JG
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);
f7c3ffd7
JG
364 ret = rmdir(test_directory);
365 ok(ret == 0, "Test directory is empty");
366 free(test_directory);
367 free(unlinked_files_directory);
9ca3e8a2
JG
368}
369
370/*
371 * Validate that the tracker refuses to track two identical unsuspendable
372 * file descriptors.
373 */
374static
375void test_unsuspendable_close_untracked(void)
376{
377 int ret, stdout_fd = fileno(stdout), unknown_fds[2], out_fd;
378 struct fd_tracker *tracker;
f7c3ffd7 379 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2 380
f7c3ffd7
JG
381 get_temporary_directories(&test_directory, &unlinked_files_directory);
382
383 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2 384 if (!tracker) {
f6bef966 385 goto end;;
9ca3e8a2
JG
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);
f7c3ffd7
JG
406 ret = rmdir(test_directory);
407 ok(ret == 0, "Test directory is empty");
f6bef966 408end:
f7c3ffd7
JG
409 free(test_directory);
410 free(unlinked_files_directory);
9ca3e8a2
JG
411}
412
f7c3ffd7
JG
413static 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)
9ca3e8a2
JG
418{
419 int ret = 0;
420 unsigned int i;
421
422 for (i = 0; i < count; i++) {
f0faf175 423 int p_ret;
9ca3e8a2
JG
424 char *file_path;
425 struct fs_handle *handle;
426 mode_t mode = S_IWUSR | S_IRUSR;
427
f7c3ffd7 428 p_ret = asprintf(&file_path, "file-%u", i);
f0faf175 429 assert(p_ret >= 0);
9ca3e8a2
JG
430 file_paths[i] = file_path;
431
f7c3ffd7 432 handle = fd_tracker_open_fs_handle(tracker, directory, file_path,
9ca3e8a2
JG
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
f7c3ffd7
JG
443static 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)
f0faf175
JG
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
f7c3ffd7 456 handle = fd_tracker_open_fs_handle(tracker, directory, file,
f0faf175
JG
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
9ca3e8a2
JG
467static
468int 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 }
f7c3ffd7
JG
481 if (fs_handle_unlink(handles[i])) {
482 diag("Failed to unlink fs_handle to file %s", file_path);
483 ret = -1;
484 }
9ca3e8a2 485 if (fs_handle_close(handles[i])) {
f7c3ffd7 486 diag("Failed to close fs_handle to file %s", file_path);
9ca3e8a2
JG
487 ret = -1;
488 }
f7c3ffd7 489 free(file_path);
9ca3e8a2
JG
490 }
491 return ret;
492}
493
494static
495void test_suspendable_limit(void)
496{
497 int ret;
498 const int files_to_create = TRACKER_FD_LIMIT * 10;
499 struct fd_tracker *tracker;
f7c3ffd7 500 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
501 char *output_files[files_to_create];
502 struct fs_handle *handles[files_to_create];
f7c3ffd7
JG
503 struct lttng_directory_handle *dir_handle = NULL;
504 int dir_handle_fd_count;
9ca3e8a2
JG
505
506 memset(output_files, 0, sizeof(output_files));
507 memset(handles, 0, sizeof(handles));
508
f7c3ffd7
JG
509 get_temporary_directories(&test_directory, &unlinked_files_directory);
510
511 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2 512 if (!tracker) {
f6bef966 513 goto end;
9ca3e8a2
JG
514 }
515
f7c3ffd7
JG
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);
9ca3e8a2 519
f7c3ffd7 520 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
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);
f7c3ffd7
JG
524 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
525 dir_handle_fd_count);
9ca3e8a2 526
f7c3ffd7 527 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
528 output_files);
529 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
530 ret = rmdir(test_directory);
531 ok(ret == 0, "Test directory is empty");
9ca3e8a2 532 fd_tracker_destroy(tracker);
f7c3ffd7 533 lttng_directory_handle_put(dir_handle);
f6bef966 534end:
f7c3ffd7
JG
535 free(test_directory);
536 free(unlinked_files_directory);
9ca3e8a2
JG
537}
538
539static
540void test_mixed_limit(void)
541{
542 int ret;
543 const int files_to_create = TRACKER_FD_LIMIT;
544 struct fd_tracker *tracker;
f7c3ffd7 545 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
546 char *output_files[files_to_create];
547 struct fs_handle *handles[files_to_create];
f7c3ffd7
JG
548 struct lttng_directory_handle *dir_handle = NULL;
549 int dir_handle_fd_count;
9ca3e8a2
JG
550
551 memset(output_files, 0, sizeof(output_files));
552 memset(handles, 0, sizeof(handles));
553
f7c3ffd7
JG
554 get_temporary_directories(&test_directory, &unlinked_files_directory);
555
556 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2 557 if (!tracker) {
f6bef966 558 goto end;
9ca3e8a2
JG
559 }
560
f7c3ffd7
JG
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);
9ca3e8a2 564
f7c3ffd7 565 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
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);
f7c3ffd7
JG
570 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
571 dir_handle_fd_count);
9ca3e8a2
JG
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);
f7c3ffd7
JG
580 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
581 dir_handle_fd_count);
9ca3e8a2
JG
582 diag("Untrack unsuspendable file descriptors");
583 untrack_std_fds(tracker);
f7c3ffd7
JG
584 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
585 dir_handle_fd_count);
9ca3e8a2 586
f7c3ffd7 587 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
588 output_files);
589 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
590 ret = rmdir(test_directory);
591 ok(ret == 0, "Test directory is empty");
9ca3e8a2 592 fd_tracker_destroy(tracker);
f7c3ffd7 593 lttng_directory_handle_put(dir_handle);
f6bef966 594end:
f7c3ffd7
JG
595 free(test_directory);
596 free(unlinked_files_directory);
9ca3e8a2
JG
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 */
607static
608void test_suspendable_restore(void)
609{
610 int ret;
611 const int files_to_create = TRACKER_FD_LIMIT * 10;
612 struct fd_tracker *tracker;
9ca3e8a2
JG
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;
f7c3ffd7
JG
620 struct lttng_directory_handle *dir_handle = NULL;
621 int dir_handle_fd_count;
622 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
623
624 memset(output_files, 0, sizeof(output_files));
625 memset(handles, 0, sizeof(handles));
626
f7c3ffd7
JG
627 get_temporary_directories(&test_directory, &unlinked_files_directory);
628
629 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2 630 if (!tracker) {
f6bef966 631 goto end;
9ca3e8a2
JG
632 }
633
f7c3ffd7
JG
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);
9ca3e8a2 637
f7c3ffd7 638 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
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);
f7c3ffd7
JG
643 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
644 dir_handle_fd_count);
9ca3e8a2
JG
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
f7c3ffd7
JG
670 if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT +
671 unknown_fds_count +
672 dir_handle_fd_count)) {
9ca3e8a2
JG
673 fd_cap_respected = false;
674 }
675
f7c3ffd7 676 fs_handle_put_fd(handle);
9ca3e8a2
JG
677 }
678 }
679skip_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
f7c3ffd7
JG
693 fd = lttng_directory_handle_open_file(
694 dir_handle, path, O_RDONLY, 0);
9ca3e8a2
JG
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");
f7c3ffd7 731 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
732 output_files);
733 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
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);
f6bef966 738end:
f7c3ffd7
JG
739 free(test_directory);
740 free(unlinked_files_directory);
9ca3e8a2
JG
741}
742
f0faf175
JG
743static
744void test_unlink(void)
745{
746 int ret;
747 struct fd_tracker *tracker;
748 const int handles_to_open = 2;
f0faf175 749 struct fs_handle *handles[handles_to_open];
ae63e134 750 struct fs_handle *new_handle = NULL;
f0faf175 751 struct stat statbuf;
f7c3ffd7
JG
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);
f0faf175 767 if (!tracker) {
f6bef966 768 goto end;
f0faf175 769 }
f7c3ffd7
JG
770
771 dir_handle = lttng_directory_handle_create(test_directory);
772 assert(dir_handle);
f0faf175
JG
773
774 /* Open two handles to the same file. */
f7c3ffd7
JG
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);
f0faf175 779 if (ret) {
f6bef966 780 goto end;
f0faf175
JG
781 }
782
783 /*
784 * Unlinking the first handle should cause the file to be renamed
f7c3ffd7 785 * to '0'.
f0faf175
JG
786 */
787 ret = fs_handle_unlink(handles[0]);
f7c3ffd7
JG
788 ok(!ret, "Successfully unlinked the first handle to %s/%s",
789 test_directory, file_name);
f0faf175
JG
790
791 /*
792 * The original file should no longer exist on the file system, and a
f7c3ffd7 793 * new file named '0' should exist.
f0faf175 794 */
f7c3ffd7
JG
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]);
f0faf175
JG
819
820 /* The second unlink should fail with -ENOENT. */
821 ret = fs_handle_unlink(handles[1]);
f7c3ffd7
JG
822 ok(ret == -ENOENT,
823 "ENOENT is reported when attempting to unlink the second handle to %s/%s",
824 test_directory, file_name);
f0faf175
JG
825
826 /*
827 * Opening a new handle to 'my_file' should succeed.
828 */
f7c3ffd7
JG
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);
f0faf175
JG
832 assert(new_handle);
833
834 /*
835 * Unlinking the new handle should cause the file to be renamed
f7c3ffd7 836 * to '1' since '0' already exists.
f0faf175
JG
837 */
838 ret = fs_handle_unlink(new_handle);
f7c3ffd7
JG
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);
f0faf175
JG
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
f7c3ffd7
JG
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");
f6bef966 869end:
f0faf175 870 fd_tracker_destroy(tracker);
f7c3ffd7
JG
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);
f0faf175
JG
876}
877
9ca3e8a2
JG
878int 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();
ea05fafd 896 diag("Unsuspendable - check that file descriptor limit is enforced");
9ca3e8a2
JG
897 test_unsuspendable_limit();
898
ea05fafd 899 diag("Suspendable - check that file descriptor limit is enforced");
9ca3e8a2
JG
900 test_suspendable_limit();
901 diag("Suspendable - restoration test");
902 test_suspendable_restore();
903
ea05fafd 904 diag("Mixed - check that file descriptor limit is enforced");
9ca3e8a2
JG
905 test_mixed_limit();
906
f0faf175
JG
907 diag("Suspendable - Unlinking test");
908 test_unlink();
909
f7c3ffd7 910 rcu_barrier();
9ca3e8a2
JG
911 rcu_unregister_thread();
912 return exit_status();
913}
This page took 0.065291 seconds and 5 git commands to generate.