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