Maintain a channel-per-session_id hash table in the consumers
[lttng-tools.git] / src / common / compat / directory-handle.c
CommitLineData
18710679
JG
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);
46307ffe
JG
44static
45void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
18710679
JG
46
47#ifdef COMPAT_DIRFD
48
49LTTNG_HIDDEN
50int lttng_directory_handle_init(struct lttng_directory_handle *handle,
51 const char *path)
52{
53 int ret;
54
55 if (!path) {
56 handle->dirfd = AT_FDCWD;
57 ret = 0;
58 goto end;
59 }
60 ret = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
61 if (ret == -1) {
62 PERROR("Failed to initialize directory handle to \"%s\"", path);
63 goto end;
64 }
65 handle->dirfd = ret;
66 ret = 0;
67end:
68 return ret;
69}
70
71LTTNG_HIDDEN
72int lttng_directory_handle_init_from_dirfd(
73 struct lttng_directory_handle *handle, int dirfd)
74{
75 handle->dirfd = dirfd;
76 return 0;
77}
78
79LTTNG_HIDDEN
80void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
81{
82 int ret;
83
46307ffe
JG
84 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
85 goto end;
18710679
JG
86 }
87 ret = close(handle->dirfd);
88 if (ret == -1) {
89 PERROR("Failed to close directory file descriptor of directory handle");
90 }
46307ffe
JG
91end:
92 lttng_directory_handle_invalidate(handle);
18710679
JG
93}
94
578e21bd
JG
95LTTNG_HIDDEN
96int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
97 struct lttng_directory_handle *new_copy)
98{
99 int ret = 0;
100
101 if (handle->dirfd == AT_FDCWD) {
102 new_copy->dirfd = handle->dirfd;
103 } else {
104 new_copy->dirfd = dup(handle->dirfd);
105 if (new_copy->dirfd == -1) {
106 PERROR("Failed to duplicate directory fd of directory handle");
107 ret = -1;
108 }
109 }
110 return ret;
111}
112
46307ffe
JG
113static
114void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
115{
116 handle->dirfd = -1;
117}
118
18710679
JG
119static
120int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
121 const char *path, struct stat *st)
122{
123 return fstatat(handle->dirfd, path, st, 0);
124}
125
126static
127int lttng_directory_handle_mkdir(
128 const struct lttng_directory_handle *handle,
129 const char *path, mode_t mode)
130{
131 return mkdirat(handle->dirfd, path, mode);
132}
133
134static
135int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
136 mode_t mode, uid_t uid, gid_t gid)
137{
138 return run_as_mkdirat(handle->dirfd, path, mode, uid, gid);
139}
140
141static
142int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
143 const char *path, mode_t mode, uid_t uid, gid_t gid)
144{
145 return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid);
146}
147
148#else /* COMPAT_DIRFD */
149
150LTTNG_HIDDEN
151int lttng_directory_handle_init(struct lttng_directory_handle *handle,
152 const char *path)
153{
154 int ret;
155 size_t cwd_len, path_len, handle_path_len;
156 char cwd_buf[LTTNG_PATH_MAX];
157 const char *cwd;
158 bool add_slash = false;
159 struct stat stat_buf;
160
161 cwd = getcwd(cwd_buf, sizeof(cwd_buf));
162 if (!cwd) {
163 PERROR("Failed to initialize directory handle, can't get current working directory");
164 ret = -1;
165 goto end;
166 }
167 cwd_len = strlen(cwd);
168 if (cwd_len == 0) {
169 ERR("Failed to initialize directory handle to \"%s\": getcwd() returned an empty string",
170 path);
171 ret = -1;
172 goto end;
173 }
174 if (cwd[cwd_len - 1] != '/') {
175 add_slash = true;
176 }
177
178 if (path) {
179 path_len = strlen(path);
180 if (path_len == 0) {
181 ERR("Failed to initialize directory handle: provided path is an empty string");
182 ret = -1;
183 goto end;
184 }
185
186 /*
187 * Ensure that 'path' is a directory. There is a race
188 * (TOCTOU) since the directory could be removed/replaced/renamed,
189 * but this is inevitable on platforms that don't provide dirfd support.
190 */
191 ret = stat(path, &stat_buf);
192 if (ret == -1) {
193 PERROR("Failed to initialize directory handle to \"%s\", stat() failed",
194 path);
195 goto end;
196 }
197 if (!S_ISDIR(stat_buf.st_mode)) {
198 ERR("Failed to initialize directory handle to \"%s\": not a directory",
199 path);
200 ret = -1;
201 goto end;
202 }
203 if (*path == '/') {
204 handle->base_path = strdup(path);
205 if (!handle->base_path) {
206 ret = -1;
207 }
208 /* Not an error. */
209 goto end;
210 }
211 } else {
212 path = "";
213 path_len = 0;
214 add_slash = false;
215 }
216
217 handle_path_len = cwd_len + path_len + !!add_slash + 2;
218 if (handle_path_len >= LTTNG_PATH_MAX) {
219 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
220 handle_path_len, LTTNG_PATH_MAX);
221 ret = -1;
222 goto end;
223 }
224 handle->base_path = zmalloc(handle_path_len);
225 if (!handle->base_path) {
226 PERROR("Failed to initialize directory handle");
227 ret = -1;
228 goto end;
229 }
230
231 ret = sprintf(handle->base_path, "%s%s%s/", cwd,
232 add_slash ? "/" : "", path);
233 if (ret == -1 || ret >= handle_path_len) {
234 ERR("Failed to initialize directory handle: path formatting failed");
235 ret = -1;
236 goto end;
237 }
238end:
239 return ret;
240}
241
242LTTNG_HIDDEN
243int lttng_directory_handle_init_from_dirfd(
244 struct lttng_directory_handle *handle, int dirfd)
245{
246 assert(dirfd == AT_FDCWD);
247 return lttng_directory_handle_init(handle, NULL);
248}
249
250LTTNG_HIDDEN
251void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
252{
253 free(handle->base_path);
46307ffe 254 lttng_directory_handle_invalidate(handle);
18710679
JG
255}
256
578e21bd
JG
257LTTNG_HIDDEN
258int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
259 struct lttng_directory_handle *new_copy)
260{
261 new_copy->base_path = strdup(handle->base_path);
262 return new_copy->base_path ? 0 : -1;
263}
264
46307ffe
JG
265static
266void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
267{
268 handle->base_path = NULL;
269}
270
18710679
JG
271static
272int get_full_path(const struct lttng_directory_handle *handle,
273 const char *subdirectory, char *fullpath, size_t size)
274{
275 int ret;
276
277 /*
278 * Don't include the base path if subdirectory is absolute.
279 * This is the same behaviour than mkdirat.
280 */
281 ret = snprintf(fullpath, size, "%s%s",
282 *subdirectory != '/' ? handle->base_path : "",
283 subdirectory);
284 if (ret == -1 || ret >= size) {
285 ERR("Failed to format subdirectory from directory handle");
286 ret = -1;
287 }
288 ret = 0;
289 return ret;
290}
291
292static
293int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
294 const char *subdirectory, struct stat *st)
295{
296 int ret;
297 char fullpath[LTTNG_PATH_MAX];
298
299 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
300 if (ret) {
301 errno = ENOMEM;
302 goto end;
303 }
304
305 ret = stat(fullpath, st);
306end:
307 return ret;
308}
309
310static
311int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle,
312 const char *subdirectory, mode_t mode)
313{
314 int ret;
315 char fullpath[LTTNG_PATH_MAX];
316
317 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
318 if (ret) {
319 errno = ENOMEM;
320 goto end;
321 }
322
323 ret = mkdir(fullpath, mode);
324end:
325 return ret;
326}
327
328static
329int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
330 mode_t mode, uid_t uid, gid_t gid)
331{
332 int ret;
333 char fullpath[LTTNG_PATH_MAX];
334
335 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
336 if (ret) {
337 errno = ENOMEM;
338 goto end;
339 }
340
341 ret = run_as_mkdir(fullpath, mode, uid, gid);
342end:
343 return ret;
344}
345
346static
347int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
348 const char *path, mode_t mode, uid_t uid, gid_t gid)
349{
350 int ret;
351 char fullpath[LTTNG_PATH_MAX];
352
353 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
354 if (ret) {
355 errno = ENOMEM;
356 goto end;
357 }
358
359 ret = run_as_mkdir_recursive(fullpath, mode, uid, gid);
360end:
361 return ret;
362}
363
364#endif /* COMPAT_DIRFD */
365
366/*
367 * On some filesystems (e.g. nfs), mkdir will validate access rights before
368 * checking for the existence of the path element. This means that on a setup
369 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
370 * recursively creating a path of the form "/home/my_user/trace/" will fail with
371 * EACCES on mkdir("/home", ...).
372 *
373 * Checking the path for existence allows us to work around this behaviour.
374 */
375static
376int create_directory_check_exists(const struct lttng_directory_handle *handle,
377 const char *path, mode_t mode)
378{
379 int ret = 0;
380 struct stat st;
381
382 ret = lttng_directory_handle_stat(handle, path, &st);
383 if (ret == 0) {
384 if (S_ISDIR(st.st_mode)) {
385 /* Directory exists, skip. */
386 goto end;
387 } else {
388 /* Exists, but is not a directory. */
389 errno = ENOTDIR;
390 ret = -1;
391 goto end;
392 }
393 }
394
395 /*
396 * Let mkdir handle other errors as the caller expects mkdir
397 * semantics.
398 */
399 ret = lttng_directory_handle_mkdir(handle, path, mode);
400end:
401 return ret;
402}
403
404/* Common implementation. */
46307ffe
JG
405LTTNG_HIDDEN
406struct lttng_directory_handle
407lttng_directory_handle_move(struct lttng_directory_handle *original)
408{
409 const struct lttng_directory_handle tmp = *original;
410
411 lttng_directory_handle_invalidate(original);
412 return tmp;
413}
414
18710679
JG
415static
416int create_directory_recursive(const struct lttng_directory_handle *handle,
417 const char *path, mode_t mode)
418{
419 char *p, tmp[LTTNG_PATH_MAX];
420 size_t len;
421 int ret;
422
423 assert(path);
424
425 ret = lttng_strncpy(tmp, path, sizeof(tmp));
426 if (ret) {
427 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
428 strlen(path) + 1, sizeof(tmp));
429 goto error;
430 }
431
432 len = strlen(path);
433 if (tmp[len - 1] == '/') {
434 tmp[len - 1] = 0;
435 }
436
437 for (p = tmp + 1; *p; p++) {
438 if (*p == '/') {
439 *p = 0;
440 if (tmp[strlen(tmp) - 1] == '.' &&
441 tmp[strlen(tmp) - 2] == '.' &&
442 tmp[strlen(tmp) - 3] == '/') {
443 ERR("Using '/../' is not permitted in the trace path (%s)",
444 tmp);
445 ret = -1;
446 goto error;
447 }
448 ret = create_directory_check_exists(handle, tmp, mode);
449 if (ret < 0) {
450 if (errno != EACCES) {
451 PERROR("Failed to create directory \"%s\"",
452 path);
453 ret = -errno;
454 goto error;
455 }
456 }
457 *p = '/';
458 }
459 }
460
461 ret = create_directory_check_exists(handle, tmp, mode);
462 if (ret < 0) {
463 PERROR("mkdirat recursive last element");
464 ret = -errno;
465 }
466error:
467 return ret;
468}
469
470LTTNG_HIDDEN
471int lttng_directory_handle_create_subdirectory_as_user(
472 const struct lttng_directory_handle *handle,
473 const char *subdirectory,
69e3a560 474 mode_t mode, const struct lttng_credentials *creds)
18710679
JG
475{
476 int ret;
477
478 if (!creds) {
479 /* Run as current user. */
480 ret = create_directory_check_exists(handle,
481 subdirectory, mode);
482 } else {
483 ret = _run_as_mkdir(handle, subdirectory,
484 mode, creds->uid, creds->gid);
485 }
486
487 return ret;
488}
489
490LTTNG_HIDDEN
491int lttng_directory_handle_create_subdirectory_recursive_as_user(
492 const struct lttng_directory_handle *handle,
493 const char *subdirectory_path,
69e3a560 494 mode_t mode, const struct lttng_credentials *creds)
18710679
JG
495{
496 int ret;
497
498 if (!creds) {
499 /* Run as current user. */
500 ret = create_directory_recursive(handle,
501 subdirectory_path, mode);
502 } else {
503 ret = _run_as_mkdir_recursive(handle, subdirectory_path,
504 mode, creds->uid, creds->gid);
505 }
506
507 return ret;
508}
509
510LTTNG_HIDDEN
511int lttng_directory_handle_create_subdirectory(
512 const struct lttng_directory_handle *handle,
513 const char *subdirectory,
514 mode_t mode)
515{
516 return lttng_directory_handle_create_subdirectory_as_user(
517 handle, subdirectory, mode, NULL);
518}
519
520LTTNG_HIDDEN
521int lttng_directory_handle_create_subdirectory_recursive(
522 const struct lttng_directory_handle *handle,
523 const char *subdirectory_path,
524 mode_t mode)
525{
526 return lttng_directory_handle_create_subdirectory_recursive_as_user(
527 handle, subdirectory_path, mode, NULL);
528}
This page took 0.04239 seconds and 4 git commands to generate.