Extract filesystem-related functions into fs.c
[tinc] / src / fs.c
1 #include "system.h"
2
3 #include "fs.h"
4 #include "names.h"
5 #include "xalloc.h"
6
7 static bool makedir(const char *path, mode_t mode) {
8         assert(path);
9
10         if(mkdir(path, mode)) {
11                 if(errno == EEXIST) {
12                         chmod(path, mode);
13                         return true;
14                 }
15
16                 fprintf(stderr, "Could not create directory %s: %s\n", path, strerror(errno));
17                 return false;
18         }
19
20         return true;
21 }
22
23 bool makedirs(tinc_dir_t dirs) {
24         if(!dirs) {
25                 return false;
26         }
27
28         char path[PATH_MAX];
29         bool need_confbase = dirs & ~((unsigned)DIR_CONFDIR);
30
31         if(need_confbase && (!confbase || !makedir(confbase, 0755))) {
32                 return false;
33         }
34
35         if(dirs & DIR_CONFDIR && !confbase_given && (!confdir || !makedir(confdir, 0755))) {
36                 return false;
37         }
38
39         if(dirs & DIR_INVITATIONS) {
40                 snprintf(path, sizeof(path), "%s" SLASH "invitations", confbase);
41
42                 if(!makedir(path, 0700)) {
43                         return false;
44                 }
45         }
46
47         if(dirs & DIR_CACHE) {
48                 snprintf(path, sizeof(path), "%s" SLASH "cache", confbase);
49
50                 if(!makedir(path, 0755)) {
51                         return false;
52                 }
53         }
54
55         if(dirs & DIR_HOSTS) {
56                 snprintf(path, sizeof(path), "%s" SLASH "hosts", confbase);
57
58                 if(!makedir(path, 0755)) {
59                         return false;
60                 }
61         }
62
63         return true;
64 }
65
66
67 /* Open a file with the desired permissions, minus the umask.
68    Also, if we want to create an executable file, we call fchmod()
69    to set the executable bits. */
70
71 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
72         mode_t mask = umask(0);
73         perms &= ~mask;
74         umask(~perms & 0777);
75         FILE *f = fopen(filename, mode);
76
77         if(!f) {
78                 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
79                 return NULL;
80         }
81
82 #ifdef HAVE_FCHMOD
83
84         if(perms & 0444) {
85                 fchmod(fileno(f), perms);
86         }
87
88 #endif
89         umask(mask);
90         return f;
91 }
92
93 char *absolute_path(const char *path) {
94 #ifdef HAVE_WINDOWS
95         // Works for nonexistent paths
96         return _fullpath(NULL, path, 0);
97 #else
98
99         if(!path || !*path) {
100                 return NULL;
101         }
102
103         // If an absolute path was passed, return its copy
104         if(*path == '/') {
105                 return xstrdup(path);
106         }
107
108         // Try using realpath. If it fails for any reason
109         // other than that the file was not found, bail out.
110         char *abs = realpath(path, NULL);
111
112         if(abs || errno != ENOENT) {
113                 return abs;
114         }
115
116         // Since the file does not exist, we're forced to use a fallback.
117         // Get current working directory and concatenate it with the argument.
118         char cwd[PATH_MAX];
119
120         if(!getcwd(cwd, sizeof(cwd))) {
121                 return NULL;
122         }
123
124         // Remove trailing slash if present since we'll be adding our own
125         size_t cwdlen = strlen(cwd);
126
127         if(cwdlen && cwd[cwdlen - 1] == '/') {
128                 cwd[cwdlen - 1] = '\0';
129         }
130
131         // We don't do any normalization because it's complicated, and the payoff is small.
132         // If user passed something like '.././../foo' — that's their choice; fopen works either way.
133         xasprintf(&abs, "%s/%s", cwd, path);
134
135         if(strlen(abs) >= PATH_MAX) {
136                 free(abs);
137                 abs = NULL;
138         }
139
140         return abs;
141 #endif
142 }