Generate a UUID on lttng-sessiond launch
[lttng-tools.git] / src / common / compat / directory-handle.c
... / ...
CommitLineData
1/*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as published by the Free Software Foundation.
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
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18#include <common/compat/directory-handle.h>
19#include <common/error.h>
20#include <common/macros.h>
21#include <common/runas.h>
22#include <common/credentials.h>
23#include <lttng/constant.h>
24
25#include <assert.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30
31static
32int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
33 const char *path, struct stat *st);
34static
35int lttng_directory_handle_mkdir(
36 const struct lttng_directory_handle *handle,
37 const char *path, mode_t mode);
38static
39int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
40 mode_t mode, uid_t uid, gid_t gid);
41static
42int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
43 const char *path, mode_t mode, uid_t uid, gid_t gid);
44static
45void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
46
47#ifdef COMPAT_DIRFD
48
49LTTNG_HIDDEN
50int lttng_directory_handle_init(struct lttng_directory_handle *new_handle,
51 const char *path)
52{
53 const struct lttng_directory_handle cwd_handle = {
54 .dirfd = AT_FDCWD,
55 };
56
57 /* Open a handle to the CWD if NULL is passed. */
58 return lttng_directory_handle_init_from_handle(new_handle,
59 path,
60 &cwd_handle);
61}
62
63LTTNG_HIDDEN
64int lttng_directory_handle_init_from_handle(
65 struct lttng_directory_handle *new_handle, const char *path,
66 const struct lttng_directory_handle *handle)
67{
68 int ret;
69
70 if (!path) {
71 ret = lttng_directory_handle_copy(handle, new_handle);
72 goto end;
73 }
74 if (!*path) {
75 ERR("Failed to initialize directory handle: provided path is an empty string");
76 ret = -1;
77 goto end;
78 }
79 ret = openat(handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
80 if (ret == -1) {
81 PERROR("Failed to initialize directory handle to \"%s\"", path);
82 goto end;
83 }
84 new_handle->dirfd = ret;
85 ret = 0;
86end:
87 return ret;
88}
89
90LTTNG_HIDDEN
91int lttng_directory_handle_init_from_dirfd(
92 struct lttng_directory_handle *handle, int dirfd)
93{
94 handle->dirfd = dirfd;
95 return 0;
96}
97
98LTTNG_HIDDEN
99void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
100{
101 int ret;
102
103 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
104 goto end;
105 }
106 ret = close(handle->dirfd);
107 if (ret == -1) {
108 PERROR("Failed to close directory file descriptor of directory handle");
109 }
110end:
111 lttng_directory_handle_invalidate(handle);
112}
113
114LTTNG_HIDDEN
115int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
116 struct lttng_directory_handle *new_copy)
117{
118 int ret = 0;
119
120 if (handle->dirfd == AT_FDCWD) {
121 new_copy->dirfd = handle->dirfd;
122 } else {
123 new_copy->dirfd = dup(handle->dirfd);
124 if (new_copy->dirfd == -1) {
125 PERROR("Failed to duplicate directory fd of directory handle");
126 ret = -1;
127 }
128 }
129 return ret;
130}
131
132static
133void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
134{
135 handle->dirfd = -1;
136}
137
138static
139int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
140 const char *path, struct stat *st)
141{
142 return fstatat(handle->dirfd, path, st, 0);
143}
144
145static
146int lttng_directory_handle_mkdir(
147 const struct lttng_directory_handle *handle,
148 const char *path, mode_t mode)
149{
150 return mkdirat(handle->dirfd, path, mode);
151}
152
153static
154int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
155 mode_t mode, uid_t uid, gid_t gid)
156{
157 return run_as_mkdirat(handle->dirfd, path, mode, uid, gid);
158}
159
160static
161int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
162 const char *path, mode_t mode, uid_t uid, gid_t gid)
163{
164 return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid);
165}
166
167#else /* COMPAT_DIRFD */
168
169static
170int get_full_path(const struct lttng_directory_handle *handle,
171 const char *subdirectory, char *fullpath, size_t size)
172{
173 int ret;
174
175 subdirectory = subdirectory ? : "";
176 /*
177 * Don't include the base path if subdirectory is absolute.
178 * This is the same behaviour than mkdirat.
179 */
180 ret = snprintf(fullpath, size, "%s%s",
181 *subdirectory != '/' ? handle->base_path : "",
182 subdirectory);
183 if (ret == -1 || ret >= size) {
184 ERR("Failed to format subdirectory from directory handle");
185 ret = -1;
186 }
187 ret = 0;
188 return ret;
189}
190
191LTTNG_HIDDEN
192int lttng_directory_handle_init(struct lttng_directory_handle *handle,
193 const char *path)
194{
195 int ret;
196 const char *cwd;
197 size_t cwd_len, path_len;
198 char cwd_buf[LTTNG_PATH_MAX] = {};
199 char handle_buf[LTTNG_PATH_MAX] = {};
200 bool add_cwd_slash, add_trailing_slash;
201 const struct lttng_directory_handle cwd_handle = {
202 .base_path = handle_buf,
203 };
204
205 if (path && *path == '/') {
206 /*
207 * Creation of an handle to an absolute path; no need to sample
208 * the cwd.
209 */
210 goto create;
211 }
212 path_len = path ? strlen(path) : 0;
213
214 cwd = getcwd(cwd_buf, sizeof(cwd_buf));
215 if (!cwd) {
216 PERROR("Failed to initialize directory handle, can't get current working directory");
217 ret = -1;
218 goto end;
219 }
220 cwd_len = strlen(cwd);
221 if (cwd_len == 0) {
222 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
223 ret = -1;
224 goto end;
225 }
226
227 add_cwd_slash = cwd[cwd_len - 1] != '/';
228 add_trailing_slash = path && path[path_len - 1] != '/';
229
230 ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s",
231 cwd,
232 add_cwd_slash ? "/" : "",
233 path ? : "",
234 add_trailing_slash ? "/" : "");
235 if (ret == -1 || ret >= LTTNG_PATH_MAX) {
236 ERR("Failed to initialize directory handle, failed to format directory path");
237 goto end;
238 }
239create:
240 ret = lttng_directory_handle_init_from_handle(handle, path,
241 &cwd_handle);
242end:
243 return ret;
244}
245
246LTTNG_HIDDEN
247int lttng_directory_handle_init_from_handle(
248 struct lttng_directory_handle *new_handle, const char *path,
249 const struct lttng_directory_handle *handle)
250{
251 int ret;
252 size_t path_len, handle_path_len;
253 bool add_trailing_slash;
254 struct stat stat_buf;
255
256 assert(handle && handle->base_path);
257
258 ret = lttng_directory_handle_stat(handle, path, &stat_buf);
259 if (ret == -1) {
260 PERROR("Failed to create directory handle");
261 goto end;
262 } else if (!S_ISDIR(stat_buf.st_mode)) {
263 char full_path[LTTNG_PATH_MAX];
264
265 /* Best effort for logging purposes. */
266 ret = get_full_path(handle, path, full_path,
267 sizeof(full_path));
268 if (ret) {
269 full_path[0] = '\0';
270 }
271
272 ERR("Failed to initialize directory handle to \"%s\": not a directory",
273 full_path);
274 ret = -1;
275 goto end;
276 }
277 if (!path) {
278 ret = lttng_directory_handle_copy(handle, new_handle);
279 goto end;
280 }
281
282 path_len = strlen(path);
283 if (path_len == 0) {
284 ERR("Failed to initialize directory handle: provided path is an empty string");
285 ret = -1;
286 goto end;
287 }
288 if (*path == '/') {
289 new_handle->base_path = strdup(path);
290 ret = new_handle->base_path ? 0 : -1;
291 goto end;
292 }
293
294 add_trailing_slash = path[path_len - 1] != '/';
295
296 handle_path_len = strlen(handle->base_path) + path_len +
297 !!add_trailing_slash;
298 if (handle_path_len >= LTTNG_PATH_MAX) {
299 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
300 handle_path_len, LTTNG_PATH_MAX);
301 ret = -1;
302 goto end;
303 }
304 new_handle->base_path = zmalloc(handle_path_len);
305 if (!new_handle->base_path) {
306 PERROR("Failed to initialize directory handle");
307 ret = -1;
308 goto end;
309 }
310
311 ret = sprintf(new_handle->base_path, "%s%s%s",
312 handle->base_path,
313 path,
314 add_trailing_slash ? "/" : "");
315 if (ret == -1 || ret >= handle_path_len) {
316 ERR("Failed to initialize directory handle: path formatting failed");
317 ret = -1;
318 goto end;
319 }
320end:
321 return ret;
322}
323
324LTTNG_HIDDEN
325int lttng_directory_handle_init_from_dirfd(
326 struct lttng_directory_handle *handle, int dirfd)
327{
328 assert(dirfd == AT_FDCWD);
329 return lttng_directory_handle_init(handle, NULL);
330}
331
332LTTNG_HIDDEN
333void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
334{
335 free(handle->base_path);
336 lttng_directory_handle_invalidate(handle);
337}
338
339LTTNG_HIDDEN
340int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
341 struct lttng_directory_handle *new_copy)
342{
343 new_copy->base_path = strdup(handle->base_path);
344 return new_copy->base_path ? 0 : -1;
345}
346
347static
348void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
349{
350 handle->base_path = NULL;
351}
352
353static
354int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
355 const char *subdirectory, struct stat *st)
356{
357 int ret;
358 char fullpath[LTTNG_PATH_MAX];
359
360 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
361 if (ret) {
362 errno = ENOMEM;
363 goto end;
364 }
365
366 ret = stat(fullpath, st);
367end:
368 return ret;
369}
370
371static
372int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle,
373 const char *subdirectory, mode_t mode)
374{
375 int ret;
376 char fullpath[LTTNG_PATH_MAX];
377
378 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
379 if (ret) {
380 errno = ENOMEM;
381 goto end;
382 }
383
384 ret = mkdir(fullpath, mode);
385end:
386 return ret;
387}
388
389static
390int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
391 mode_t mode, uid_t uid, gid_t gid)
392{
393 int ret;
394 char fullpath[LTTNG_PATH_MAX];
395
396 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
397 if (ret) {
398 errno = ENOMEM;
399 goto end;
400 }
401
402 ret = run_as_mkdir(fullpath, mode, uid, gid);
403end:
404 return ret;
405}
406
407static
408int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
409 const char *path, mode_t mode, uid_t uid, gid_t gid)
410{
411 int ret;
412 char fullpath[LTTNG_PATH_MAX];
413
414 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
415 if (ret) {
416 errno = ENOMEM;
417 goto end;
418 }
419
420 ret = run_as_mkdir_recursive(fullpath, mode, uid, gid);
421end:
422 return ret;
423}
424
425#endif /* COMPAT_DIRFD */
426
427/*
428 * On some filesystems (e.g. nfs), mkdir will validate access rights before
429 * checking for the existence of the path element. This means that on a setup
430 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
431 * recursively creating a path of the form "/home/my_user/trace/" will fail with
432 * EACCES on mkdir("/home", ...).
433 *
434 * Checking the path for existence allows us to work around this behaviour.
435 */
436static
437int create_directory_check_exists(const struct lttng_directory_handle *handle,
438 const char *path, mode_t mode)
439{
440 int ret = 0;
441 struct stat st;
442
443 ret = lttng_directory_handle_stat(handle, path, &st);
444 if (ret == 0) {
445 if (S_ISDIR(st.st_mode)) {
446 /* Directory exists, skip. */
447 goto end;
448 } else {
449 /* Exists, but is not a directory. */
450 errno = ENOTDIR;
451 ret = -1;
452 goto end;
453 }
454 }
455
456 /*
457 * Let mkdir handle other errors as the caller expects mkdir
458 * semantics.
459 */
460 ret = lttng_directory_handle_mkdir(handle, path, mode);
461end:
462 return ret;
463}
464
465/* Common implementation. */
466LTTNG_HIDDEN
467struct lttng_directory_handle
468lttng_directory_handle_move(struct lttng_directory_handle *original)
469{
470 const struct lttng_directory_handle tmp = *original;
471
472 lttng_directory_handle_invalidate(original);
473 return tmp;
474}
475
476static
477int create_directory_recursive(const struct lttng_directory_handle *handle,
478 const char *path, mode_t mode)
479{
480 char *p, tmp[LTTNG_PATH_MAX];
481 size_t len;
482 int ret;
483
484 assert(path);
485
486 ret = lttng_strncpy(tmp, path, sizeof(tmp));
487 if (ret) {
488 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
489 strlen(path) + 1, sizeof(tmp));
490 goto error;
491 }
492
493 len = strlen(path);
494 if (tmp[len - 1] == '/') {
495 tmp[len - 1] = 0;
496 }
497
498 for (p = tmp + 1; *p; p++) {
499 if (*p == '/') {
500 *p = 0;
501 if (tmp[strlen(tmp) - 1] == '.' &&
502 tmp[strlen(tmp) - 2] == '.' &&
503 tmp[strlen(tmp) - 3] == '/') {
504 ERR("Using '/../' is not permitted in the trace path (%s)",
505 tmp);
506 ret = -1;
507 goto error;
508 }
509 ret = create_directory_check_exists(handle, tmp, mode);
510 if (ret < 0) {
511 if (errno != EACCES) {
512 PERROR("Failed to create directory \"%s\"",
513 path);
514 ret = -errno;
515 goto error;
516 }
517 }
518 *p = '/';
519 }
520 }
521
522 ret = create_directory_check_exists(handle, tmp, mode);
523 if (ret < 0) {
524 PERROR("mkdirat recursive last element");
525 ret = -errno;
526 }
527error:
528 return ret;
529}
530
531LTTNG_HIDDEN
532int lttng_directory_handle_create_subdirectory_as_user(
533 const struct lttng_directory_handle *handle,
534 const char *subdirectory,
535 mode_t mode, const struct lttng_credentials *creds)
536{
537 int ret;
538
539 if (!creds) {
540 /* Run as current user. */
541 ret = create_directory_check_exists(handle,
542 subdirectory, mode);
543 } else {
544 ret = _run_as_mkdir(handle, subdirectory,
545 mode, creds->uid, creds->gid);
546 }
547
548 return ret;
549}
550
551LTTNG_HIDDEN
552int lttng_directory_handle_create_subdirectory_recursive_as_user(
553 const struct lttng_directory_handle *handle,
554 const char *subdirectory_path,
555 mode_t mode, const struct lttng_credentials *creds)
556{
557 int ret;
558
559 if (!creds) {
560 /* Run as current user. */
561 ret = create_directory_recursive(handle,
562 subdirectory_path, mode);
563 } else {
564 ret = _run_as_mkdir_recursive(handle, subdirectory_path,
565 mode, creds->uid, creds->gid);
566 }
567
568 return ret;
569}
570
571LTTNG_HIDDEN
572int lttng_directory_handle_create_subdirectory(
573 const struct lttng_directory_handle *handle,
574 const char *subdirectory,
575 mode_t mode)
576{
577 return lttng_directory_handle_create_subdirectory_as_user(
578 handle, subdirectory, mode, NULL);
579}
580
581LTTNG_HIDDEN
582int lttng_directory_handle_create_subdirectory_recursive(
583 const struct lttng_directory_handle *handle,
584 const char *subdirectory_path,
585 mode_t mode)
586{
587 return lttng_directory_handle_create_subdirectory_recursive_as_user(
588 handle, subdirectory_path, mode, NULL);
589}
This page took 0.026254 seconds and 4 git commands to generate.