Fix: statements with side-effects in assert statements
[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
af8cf559 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
c86f3ebc 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
9cde44b6 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);
9cde44b6 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);
e0ad3e31
FD
390 ret = close(unknown_fds[0]);
391 assert(ret == 0);
392 ret = close(unknown_fds[1]);
393 assert(ret == 0);
9ca3e8a2
JG
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);
f7c3ffd7
JG
408 ret = rmdir(test_directory);
409 ok(ret == 0, "Test directory is empty");
f6bef966 410end:
f7c3ffd7
JG
411 free(test_directory);
412 free(unlinked_files_directory);
9ca3e8a2
JG
413}
414
f7c3ffd7
JG
415static 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)
9ca3e8a2
JG
420{
421 int ret = 0;
422 unsigned int i;
423
424 for (i = 0; i < count; i++) {
f0faf175 425 int p_ret;
9ca3e8a2
JG
426 char *file_path;
427 struct fs_handle *handle;
428 mode_t mode = S_IWUSR | S_IRUSR;
429
f7c3ffd7 430 p_ret = asprintf(&file_path, "file-%u", i);
f0faf175 431 assert(p_ret >= 0);
9ca3e8a2
JG
432 file_paths[i] = file_path;
433
f7c3ffd7 434 handle = fd_tracker_open_fs_handle(tracker, directory, file_path,
9ca3e8a2
JG
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
f7c3ffd7
JG
445static 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)
f0faf175
JG
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
f7c3ffd7 458 handle = fd_tracker_open_fs_handle(tracker, directory, file,
f0faf175
JG
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
9ca3e8a2
JG
469static
470int 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 }
f7c3ffd7
JG
483 if (fs_handle_unlink(handles[i])) {
484 diag("Failed to unlink fs_handle to file %s", file_path);
485 ret = -1;
486 }
9ca3e8a2 487 if (fs_handle_close(handles[i])) {
f7c3ffd7 488 diag("Failed to close fs_handle to file %s", file_path);
9ca3e8a2
JG
489 ret = -1;
490 }
f7c3ffd7 491 free(file_path);
9ca3e8a2
JG
492 }
493 return ret;
494}
495
496static
497void test_suspendable_limit(void)
498{
499 int ret;
500 const int files_to_create = TRACKER_FD_LIMIT * 10;
501 struct fd_tracker *tracker;
f7c3ffd7 502 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
503 char *output_files[files_to_create];
504 struct fs_handle *handles[files_to_create];
f7c3ffd7
JG
505 struct lttng_directory_handle *dir_handle = NULL;
506 int dir_handle_fd_count;
9ca3e8a2
JG
507
508 memset(output_files, 0, sizeof(output_files));
509 memset(handles, 0, sizeof(handles));
510
f7c3ffd7
JG
511 get_temporary_directories(&test_directory, &unlinked_files_directory);
512
513 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2 514 if (!tracker) {
f6bef966 515 goto end;
9ca3e8a2
JG
516 }
517
f7c3ffd7
JG
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);
9ca3e8a2 521
f7c3ffd7 522 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
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);
f7c3ffd7
JG
526 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
527 dir_handle_fd_count);
9ca3e8a2 528
f7c3ffd7 529 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
530 output_files);
531 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
532 ret = rmdir(test_directory);
533 ok(ret == 0, "Test directory is empty");
9ca3e8a2 534 fd_tracker_destroy(tracker);
f7c3ffd7 535 lttng_directory_handle_put(dir_handle);
f6bef966 536end:
f7c3ffd7
JG
537 free(test_directory);
538 free(unlinked_files_directory);
9ca3e8a2
JG
539}
540
541static
542void test_mixed_limit(void)
543{
544 int ret;
545 const int files_to_create = TRACKER_FD_LIMIT;
546 struct fd_tracker *tracker;
f7c3ffd7 547 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
548 char *output_files[files_to_create];
549 struct fs_handle *handles[files_to_create];
f7c3ffd7
JG
550 struct lttng_directory_handle *dir_handle = NULL;
551 int dir_handle_fd_count;
9ca3e8a2
JG
552
553 memset(output_files, 0, sizeof(output_files));
554 memset(handles, 0, sizeof(handles));
555
f7c3ffd7
JG
556 get_temporary_directories(&test_directory, &unlinked_files_directory);
557
558 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2 559 if (!tracker) {
f6bef966 560 goto end;
9ca3e8a2
JG
561 }
562
f7c3ffd7
JG
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);
9ca3e8a2 566
f7c3ffd7 567 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
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);
f7c3ffd7
JG
572 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
573 dir_handle_fd_count);
9ca3e8a2
JG
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);
f7c3ffd7
JG
582 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
583 dir_handle_fd_count);
9ca3e8a2
JG
584 diag("Untrack unsuspendable file descriptors");
585 untrack_std_fds(tracker);
f7c3ffd7
JG
586 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
587 dir_handle_fd_count);
9ca3e8a2 588
f7c3ffd7 589 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
590 output_files);
591 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
592 ret = rmdir(test_directory);
593 ok(ret == 0, "Test directory is empty");
9ca3e8a2 594 fd_tracker_destroy(tracker);
f7c3ffd7 595 lttng_directory_handle_put(dir_handle);
f6bef966 596end:
f7c3ffd7
JG
597 free(test_directory);
598 free(unlinked_files_directory);
9ca3e8a2
JG
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 */
609static
610void test_suspendable_restore(void)
611{
612 int ret;
613 const int files_to_create = TRACKER_FD_LIMIT * 10;
614 struct fd_tracker *tracker;
9ca3e8a2
JG
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;
f7c3ffd7
JG
622 struct lttng_directory_handle *dir_handle = NULL;
623 int dir_handle_fd_count;
624 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
625
626 memset(output_files, 0, sizeof(output_files));
627 memset(handles, 0, sizeof(handles));
628
f7c3ffd7
JG
629 get_temporary_directories(&test_directory, &unlinked_files_directory);
630
631 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2 632 if (!tracker) {
f6bef966 633 goto end;
9ca3e8a2
JG
634 }
635
f7c3ffd7
JG
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);
9ca3e8a2 639
f7c3ffd7 640 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
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);
f7c3ffd7
JG
645 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
646 dir_handle_fd_count);
9ca3e8a2
JG
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
f7c3ffd7
JG
672 if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT +
673 unknown_fds_count +
674 dir_handle_fd_count)) {
9ca3e8a2
JG
675 fd_cap_respected = false;
676 }
677
f7c3ffd7 678 fs_handle_put_fd(handle);
9ca3e8a2
JG
679 }
680 }
681skip_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
f7c3ffd7
JG
695 fd = lttng_directory_handle_open_file(
696 dir_handle, path, O_RDONLY, 0);
9ca3e8a2
JG
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");
f7c3ffd7 733 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
734 output_files);
735 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
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);
f6bef966 740end:
f7c3ffd7
JG
741 free(test_directory);
742 free(unlinked_files_directory);
9ca3e8a2
JG
743}
744
f0faf175
JG
745static
746void test_unlink(void)
747{
748 int ret;
749 struct fd_tracker *tracker;
750 const int handles_to_open = 2;
f0faf175 751 struct fs_handle *handles[handles_to_open];
ae63e134 752 struct fs_handle *new_handle = NULL;
f0faf175 753 struct stat statbuf;
f7c3ffd7
JG
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);
f0faf175 769 if (!tracker) {
f6bef966 770 goto end;
f0faf175 771 }
f7c3ffd7
JG
772
773 dir_handle = lttng_directory_handle_create(test_directory);
774 assert(dir_handle);
f0faf175
JG
775
776 /* Open two handles to the same file. */
f7c3ffd7
JG
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);
f0faf175 781 if (ret) {
f6bef966 782 goto end;
f0faf175
JG
783 }
784
785 /*
786 * Unlinking the first handle should cause the file to be renamed
f7c3ffd7 787 * to '0'.
f0faf175
JG
788 */
789 ret = fs_handle_unlink(handles[0]);
f7c3ffd7
JG
790 ok(!ret, "Successfully unlinked the first handle to %s/%s",
791 test_directory, file_name);
f0faf175
JG
792
793 /*
794 * The original file should no longer exist on the file system, and a
f7c3ffd7 795 * new file named '0' should exist.
f0faf175 796 */
f7c3ffd7
JG
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]);
f0faf175
JG
821
822 /* The second unlink should fail with -ENOENT. */
823 ret = fs_handle_unlink(handles[1]);
f7c3ffd7
JG
824 ok(ret == -ENOENT,
825 "ENOENT is reported when attempting to unlink the second handle to %s/%s",
826 test_directory, file_name);
f0faf175
JG
827
828 /*
829 * Opening a new handle to 'my_file' should succeed.
830 */
f7c3ffd7
JG
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);
f0faf175
JG
834 assert(new_handle);
835
836 /*
837 * Unlinking the new handle should cause the file to be renamed
f7c3ffd7 838 * to '1' since '0' already exists.
f0faf175
JG
839 */
840 ret = fs_handle_unlink(new_handle);
f7c3ffd7
JG
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);
f0faf175
JG
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
f7c3ffd7
JG
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");
f6bef966 871end:
f0faf175 872 fd_tracker_destroy(tracker);
f7c3ffd7
JG
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);
f0faf175
JG
878}
879
9ca3e8a2
JG
880int 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();
9cde44b6 898 diag("Unsuspendable - check that file descriptor limit is enforced");
9ca3e8a2
JG
899 test_unsuspendable_limit();
900
9cde44b6 901 diag("Suspendable - check that file descriptor limit is enforced");
9ca3e8a2
JG
902 test_suspendable_limit();
903 diag("Suspendable - restoration test");
904 test_suspendable_restore();
905
9cde44b6 906 diag("Mixed - check that file descriptor limit is enforced");
9ca3e8a2
JG
907 test_mixed_limit();
908
f0faf175
JG
909 diag("Suspendable - Unlinking test");
910 test_unlink();
911
f7c3ffd7 912 rcu_barrier();
9ca3e8a2
JG
913 rcu_unregister_thread();
914 return exit_status();
915}
This page took 0.089108 seconds and 4 git commands to generate.