1 /*
2 * Copyright (C) Tildeslash Ltd. All rights reserved.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Affero General Public License for more details.
11 *
12 * You should have received a copy of the GNU Affero General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * In addition, as a special exception, the copyright holders give
16 * permission to link the code of portions of this program with the
17 * OpenSSL library under certain conditions as described in each
18 * individual source file, and distribute linked combinations
19 * including the two.
20 *
21 * You must obey the GNU Affero General Public License in all respects
22 * for all of the code used other than OpenSSL.
23 */
24
25
26 #include "Config.h"
27
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <time.h>
36 #include <ctype.h>
37
38 #include "Str.h"
39 #include "system/System.h"
40 #include "File.h"
41
42
43 /**
44 * Implementation of the File Facade for Unix systems.
45 *
46 * @author http://www.tildeslash.com/
47 * @see http://www.mmonit.com/
48 * @file
49 */
50
51
52 /* ----------------------------------------------------------- Definitions */
53
54
55 #define DEFAULT_PERM 0666
56
57 const char SEPARATOR_CHAR = '/';
58 const char *SEPARATOR = "/";
59 const char PATH_SEPARATOR_CHAR = ':';
60 const char *PATH_SEPARATOR = ":";
61
62
63 /* ---------------------------------------------------------------- Public */
64
65
66
File_open(const char * file,const char * mode)67 int File_open(const char *file, const char *mode) {
68 if (file && mode) {
69 switch (mode[0]) {
70 case 'r':
71 switch (mode[1]) {
72 case '+': return open(file, O_RDWR|O_NONBLOCK);
73 default: return open(file, O_RDONLY|O_NONBLOCK);
74 }
75 case 'w':
76 switch (mode[1]) {
77 case '+': return open(file, O_CREAT|O_RDWR|O_TRUNC|O_NONBLOCK, DEFAULT_PERM);
78 default: return open(file, O_CREAT|O_WRONLY|O_TRUNC|O_NONBLOCK, DEFAULT_PERM);
79 }
80 case 'a':
81 switch (mode[1]) {
82 case '+': return open(file, O_CREAT|O_RDWR|O_APPEND|O_NONBLOCK, DEFAULT_PERM);
83 default: return open(file, O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK, DEFAULT_PERM);
84 }
85 }
86 }
87 errno = EINVAL;
88 return -1;
89 }
90
91
File_close(int fd)92 bool File_close(int fd) {
93 int r;
94 do
95 r = close(fd);
96 while (r == -1 && errno == EINTR);
97 return (r == 0);
98 }
99
100
File_rewind(int fd)101 bool File_rewind(int fd) {
102 return (lseek(fd, 0, SEEK_SET) >=0);
103 }
104
105
File_mtime(const char * file)106 time_t File_mtime(const char *file) {
107 if (file) {
108 struct stat buf;
109 if (stat(file, &buf) == 0)
110 return buf.st_mtime;
111 }
112 return -1;
113 }
114
115
File_ctime(const char * file)116 time_t File_ctime(const char *file) {
117 if (file) {
118 struct stat buf;
119 if (stat(file, &buf) == 0)
120 return buf.st_ctime;
121 }
122 return -1;
123 }
124
125
File_atime(const char * file)126 time_t File_atime(const char *file) {
127 if (file) {
128 struct stat buf;
129 if (stat(file, &buf) == 0)
130 return buf.st_atime;
131 }
132 return -1;
133 }
134
135
File_isFile(const char * file)136 bool File_isFile(const char *file) {
137 if (file) {
138 struct stat buf;
139 return (stat(file, &buf) == 0 && S_ISREG(buf.st_mode));
140 }
141 return false;
142 }
143
144
File_isSocket(const char * file)145 bool File_isSocket(const char *file) {
146 if (file) {
147 struct stat buf;
148 return (stat(file, &buf) == 0 && S_ISSOCK(buf.st_mode));
149 }
150 return false;
151 }
152
153
File_isDirectory(const char * file)154 bool File_isDirectory(const char *file) {
155 if (file) {
156 struct stat buf;
157 return (stat(file, &buf) == 0 && S_ISDIR(buf.st_mode));
158 }
159 return false;
160 }
161
162
File_exist(const char * file)163 bool File_exist(const char *file) {
164 if (file) {
165 struct stat buf;
166 return (stat(file, &buf) == 0);
167 }
168 return false;
169 }
170
171
File_type(const char * file)172 char File_type(const char *file) {
173 if (file) {
174 struct stat buf;
175 if (stat(file, &buf) == 0)
176 switch ((buf.st_mode) & S_IFMT) {
177 case S_IFREG: return 'r';
178 case S_IFDIR: return 'd';
179 case S_IFCHR: return 'c';
180 case S_IFBLK: return 'b';
181 case S_IFLNK: return 'l';
182 case S_IFIFO: return 'p';
183 case S_IFSOCK: return 's';
184 default: return '?';
185 }
186 }
187 return '?';
188 }
189
File_size(const char * file)190 off_t File_size(const char *file) {
191 if (file) {
192 struct stat buf;
193 if (stat(file, &buf) < 0)
194 return -1;
195 return buf.st_size;
196 }
197 return -1;
198 }
199
200
File_chmod(const char * file,mode_t mode)201 bool File_chmod(const char *file, mode_t mode) {
202 if (file)
203 return (chmod(file, mode) == 0);
204 errno = EINVAL;
205 return false;
206 }
207
208
File_mod(const char * file)209 int File_mod(const char *file) {
210 if (file) {
211 struct stat buf;
212 if (stat(file, &buf) == 0)
213 return buf.st_mode;
214 }
215 return -1;
216 }
217
218
File_umask(void)219 int File_umask(void) {
220 mode_t omask = umask(0);
221 umask(omask);
222 return omask;
223 }
224
225
File_setUmask(mode_t mask)226 mode_t File_setUmask(mode_t mask) {
227 mode_t omask = umask(mask);
228 return omask;
229 }
230
231
File_isReadable(const char * file)232 bool File_isReadable(const char *file) {
233 if (file)
234 return (access(file, R_OK) == 0);
235 return false;
236 }
237
238
File_isWritable(const char * file)239 bool File_isWritable(const char *file) {
240 if (file)
241 return (access(file, W_OK) == 0);
242 return false;
243 }
244
245
File_isExecutable(const char * file)246 bool File_isExecutable(const char *file) {
247 if (file)
248 return (access(file, X_OK) == 0);
249 return false;
250 }
251
252
File_delete(const char * file)253 bool File_delete(const char *file) {
254 if (file)
255 return (remove(file) == 0);
256 errno = ENOENT;
257 return false;
258 }
259
260
File_rename(const char * file,const char * name)261 bool File_rename(const char *file, const char *name) {
262 if (file)
263 return (rename(file, name) == 0);
264 errno = ENOENT;
265 return false;
266 }
267
268
File_basename(const char * path)269 const char *File_basename(const char *path) {
270 if ((STR_DEF(path))) {
271 char *f = strrchr(path, SEPARATOR_CHAR);
272 return (f ? ++f : path);
273 }
274 return path;
275 }
276
277
File_dirname(char * path)278 char *File_dirname(char *path) {
279 if ((STR_DEF(path))) {
280 char *d = strrchr(path, SEPARATOR_CHAR);
281 if (d)
282 *(d + 1) = 0; /* Keep last separator */
283 else {
284 path[0] = '.';
285 path[1] = 0;
286 }
287 }
288 return path;
289 }
290
291
File_extension(const char * path)292 const char *File_extension(const char *path) {
293 if (STR_DEF(path)) {
294 char *e = strrchr(path, '.');
295 return (e ? ++e : NULL);
296 }
297 return NULL;
298 }
299
300
File_removeTrailingSeparator(char * path)301 char *File_removeTrailingSeparator(char *path) {
302 if (STR_DEF(path)) {
303 char *p;
304 for (p = path; *p; p++);
305 do
306 *(p--) = 0;
307 while ((p > path) && (isspace(*p) || *p == SEPARATOR_CHAR));
308 }
309 return path;
310 }
311
312
File_getRealPath(const char * path,char * resolved)313 char *File_getRealPath(const char *path, char *resolved) {
314 if (path && resolved)
315 return realpath(path, resolved);
316 return NULL;
317 }
318