Branch data Line data Source code
1 : : #include "precizer.h"
2 : : #include <errno.h>
3 : : #include <fcntl.h>
4 : :
5 : : /**
6 : : * @brief Open a directory as the base for relative path access checks
7 : : *
8 : : * @details
9 : : * Uses the platform search-only directory mode when available. macOS and
10 : : * Cygwin provide `O_SEARCH`, while Linux provides `O_PATH`. The fallback uses
11 : : * a read-only directory descriptor for platforms without either extension
12 : : *
13 : : * `O_SEARCH` and `O_PATH` allow the descriptor to serve as a path-resolution
14 : : * base without requiring directory enumeration. The descriptor is opened with
15 : : * close-on-exec and is verified to refer to a directory
16 : : *
17 : : * Test builds can force an unavailable result for a matching path. The test
18 : : * override also sets a representative `errno` value so callers exercise the
19 : : * same diagnostics as a real `open()` failure
20 : : *
21 : : * @param[in] directory_path Descriptor containing the directory path to open
22 : : * @param[out] directory_fd_out Receives the opened descriptor. Set to -1 before
23 : : * `open()` is attempted
24 : : * @return `FILE_ACCESS_ALLOWED` when the directory was opened.
25 : : * `FILE_NOT_FOUND` or `FILE_ACCESS_DENIED` for the corresponding
26 : : * `open()` failure. `FILE_ACCESS_ERROR` when the output pointer is
27 : : * invalid or another `open()` failure cannot be classified more
28 : : * specifically
29 : : */
30 : 529 : FileAccessStatus directory_open(
31 : : const memory *directory_path,
32 : : int *directory_fd_out)
33 : : {
34 [ - + ]: 529 : if(directory_fd_out == NULL)
35 : : {
36 : 0 : errno = EINVAL;
37 : 0 : return(FILE_ACCESS_ERROR);
38 : : }
39 : :
40 : 529 : *directory_fd_out = -1;
41 : :
42 : 529 : const char *runtime_directory_path = m_text(directory_path);
43 : :
44 : : #ifdef TESTITALL_TEST_HOOKS
45 : 529 : FileAccessStatus forced_status = FILE_ACCESS_ALLOWED;
46 : :
47 [ + + ]: 529 : if(testitall_file_access_status_override(runtime_directory_path,&forced_status) == true
48 [ + - ]: 8 : && forced_status != FILE_ACCESS_ALLOWED)
49 : : {
50 [ + + ]: 8 : if(forced_status == FILE_ACCESS_DENIED)
51 : : {
52 : 4 : errno = EACCES;
53 : :
54 [ + + ]: 4 : } else if(forced_status == FILE_NOT_FOUND){
55 : 2 : errno = ENOENT;
56 : :
57 : : } else {
58 : 2 : errno = EIO;
59 : : }
60 : :
61 : 8 : return(forced_status);
62 : : }
63 : : #endif
64 : :
65 : 521 : int open_flags = O_DIRECTORY | O_CLOEXEC;
66 : :
67 : : /*
68 : : * Open the directory with the least access required to use it as the starting
69 : : * point for relative paths:
70 : : *
71 : : * - O_SEARCH is the POSIX-style option for opening a directory so known child
72 : : * names can be resolved without asking permission to list its contents.
73 : : * - O_PATH is the Linux alternative. It creates a lightweight descriptor that
74 : : * refers to the directory and can be used by functions such as faccessat().
75 : : * - O_RDONLY is the portable fallback when neither specialized option exists.
76 : : * It may require directory read permission even though this function does
77 : : * not need to enumerate the directory
78 : : */
79 : : #if defined(O_SEARCH)
80 : : open_flags |= O_SEARCH;
81 : : #elif defined(O_PATH)
82 : 521 : open_flags |= O_PATH;
83 : : #else
84 : : open_flags |= O_RDONLY;
85 : : #endif
86 : :
87 : 521 : const int directory_fd = open(runtime_directory_path,open_flags);
88 : :
89 [ + - ]: 521 : if(directory_fd >= 0)
90 : : {
91 : 521 : *directory_fd_out = directory_fd;
92 : 521 : return(FILE_ACCESS_ALLOWED);
93 : : }
94 : :
95 : 0 : return(file_access_status(errno));
96 : : }
|