source: branches/stable/CORE/arb_file.cxx

Last change on this file was 14505, checked in by westram, 4 years ago
File size: 11.1 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : arb_file.cxx                                      //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Coded by Ralf Westram (coder@reallysoft.de) in October 2011   //
7//   Institute of Microbiology (Technical University Munich)       //
8//   http://www.arb-home.de/                                       //
9//                                                                 //
10// =============================================================== //
11
12#include "arb_file.h"
13#include <arb_assert.h>
14
15#include <unistd.h>
16#include <utime.h>
17#include <sys/stat.h>
18#include <cstdio>
19#include <cerrno>
20
21#include "arb_msg.h"
22
23// AISC_MKPT_PROMOTE:#ifndef ARB_CORE_H
24// AISC_MKPT_PROMOTE:#include "arb_core.h"
25// AISC_MKPT_PROMOTE:#endif
26
27long GB_size_of_file(const char *path) {
28    struct stat stt;
29    if (!path || stat(path, &stt)) return -1;
30    return stt.st_size;
31}
32
33long GB_size_of_FILE(FILE *in) {
34    int         fi = fileno(in);
35    struct stat st;
36    if (fstat(fi, &st)) {
37        GB_export_error("GB_size_of_FILE: sorry file is not readable");
38        return -1;
39    }
40    return st.st_size;
41}
42
43unsigned long GB_time_of_file(const char *path) {
44    struct stat stt;
45    if (!path || stat(path, &stt)) return 0; // return epoch for missing files
46    return stt.st_mtime;
47}
48
49GB_ERROR GB_set_time_of_file(const char *path, unsigned long new_time) {
50    utimbuf ut;
51
52    ut.actime  = new_time;
53    ut.modtime = new_time;
54
55    int res = utime(path, &ut);
56    return res ? GB_IO_error("setting timestamp of", path) : NULL;
57}
58
59long GB_mode_of_file(const char *path) {
60    if (path) {
61        struct stat stt;
62        if (stat(path, &stt) == 0) return stt.st_mode;
63    }
64    return -1;
65}
66
67long GB_mode_of_link(const char *path) {
68    if (path) {
69        struct stat stt;
70        if (lstat(path, &stt) == 0) return stt.st_mode;
71    }
72    return -1;
73}
74
75bool GB_is_regularfile(const char *path) {
76    // Warning : returns true for symbolic links to files (use GB_is_link() to test)
77    if (!path) return false;
78    struct stat stt;
79    return stat(path, &stt) == 0 && S_ISREG(stt.st_mode);
80}
81
82bool GB_is_link(const char *path) {
83    if (!path) return false;
84    struct stat stt;
85    return lstat(path, &stt) == 0 && S_ISLNK(stt.st_mode);
86}
87
88bool GB_is_executablefile(const char *path) {
89    struct stat stt;
90    bool        executable = false;
91
92    if (stat(path, &stt) == 0) {
93        uid_t my_userid = geteuid(); // effective user id
94        if (stt.st_uid == my_userid) { // I am the owner of the file
95            executable = !!(stt.st_mode&S_IXUSR); // owner execution permission
96        }
97        else {
98            gid_t my_groupid = getegid(); // effective group id
99            if (stt.st_gid == my_groupid) { // I am member of the file's group
100                executable = !!(stt.st_mode&S_IXGRP); // group execution permission
101            }
102            else {
103                executable = !!(stt.st_mode&S_IXOTH); // others execution permission
104            }
105        }
106    }
107
108    return executable;
109}
110
111bool GB_is_privatefile(const char *path, bool read_private) {
112    // return true, if nobody but user has write permission
113    // if 'read_private' is true, only return true if nobody but user has read permission
114    //
115    // Note: Always returns true for missing files!
116    //
117    // GB_is_privatefile is mainly used to assert that files generated in /tmp have secure permissions
118
119    struct stat stt;
120    bool        isprivate = true;
121
122    if (stat(path, &stt) == 0) {
123        if (read_private) {
124            isprivate = (stt.st_mode & (S_IWGRP|S_IWOTH|S_IRGRP|S_IROTH)) == 0;
125        }
126        else {
127            isprivate = (stt.st_mode & (S_IWGRP|S_IWOTH)) == 0;
128        }
129    }
130    return isprivate;
131}
132
133inline bool mode_is_user_writeable(long mode) { return mode>0 && (mode & S_IWUSR); }
134
135bool GB_is_writeablefile(const char *filename) { // for user
136    bool writable = false;
137    if (GB_is_regularfile(filename)) {
138        writable = mode_is_user_writeable(GB_mode_of_file(filename));
139        if (writable && GB_is_link(filename)) {
140            char *target = GB_follow_unix_link(filename);
141            writable     = GB_is_writeablefile(target);
142            free(target);
143        }
144    }
145    return writable;
146}
147
148static bool GB_is_readable(const char *file_or_dir) {
149    if (file_or_dir) {
150        FILE *in = fopen(file_or_dir, "r");
151        if (in) {
152            fclose(in);
153            return true;
154        }
155    }
156    return false;
157}
158
159bool GB_is_readablefile(const char *filename) {
160    return !GB_is_directory(filename) && GB_is_readable(filename);
161}
162
163bool GB_is_directory(const char *path) {
164    // Warning : returns true for symbolic links to directories (use GB_is_link())
165    struct stat stt;
166    return path && stat(path, &stt) == 0 && S_ISDIR(stt.st_mode);
167}
168
169long GB_getuid_of_file(const char *path) {
170    struct stat stt;
171    if (stat(path, &stt)) return -1;
172    return stt.st_uid;
173}
174
175int GB_unlink(const char *path)
176{   /*! unlink a file
177     * @return
178     *  0   success
179     *  1   File did not exist
180     * -1   Error (use GB_await_error() to retrieve message)
181     */
182
183    if (unlink(path) != 0) {
184        if (errno == ENOENT) {
185            return 1;
186        }
187        GB_export_error(GB_IO_error("removing", path));
188        return -1;
189    }
190    return 0;
191}
192
193void GB_unlink_or_warn(const char *path, GB_ERROR *error) {
194    /* Unlinks 'path'
195     *
196     * In case of a real unlink failure:
197     * - if 'error' is given -> set error if not already set
198     * - otherwise only warn
199     */
200
201    if (GB_unlink(path)<0) {
202        GB_ERROR unlink_error = GB_await_error();
203        if (error && *error == NULL) *error = unlink_error;
204        else GB_warning(unlink_error);
205    }
206}
207
208GB_ERROR GB_symlink(const char *target, const char *link) {
209    GB_ERROR error = NULL;
210    if (symlink(target, link)<0) {
211        char *what = GBS_global_string_copy("creating symlink (to file '%s')", target);
212        error      = GB_IO_error(what, link);
213        free(what);
214    }
215    return error;
216}
217
218GB_ERROR GB_set_mode_of_file(const char *path, long mode) {
219    /*
220      Patch from Alan McCulloch:
221
222      get user, group,other read, write and execute
223      permissions and if these are the same in the
224      existing and requested modes of the file ,
225      don't chmod (gets around "Cannot set mode" errors
226      which will happen if user does not own file)
227
228      This assumes that the requested permission change is not
229      outside the mask S_IRWXU | S_IRWXG | S_IRWXO - if it is, then
230      the requested change will not be made
231    */
232
233    int permissions_mask = S_IRWXU | S_IRWXG | S_IRWXO ;
234    struct stat sb;
235
236
237    if (stat(path, &sb) == -1) {
238        return GBS_global_string("Cannot get existing mode of '%s'", path);
239    }
240
241    if (((int)sb.st_mode & permissions_mask) == ((int)mode & permissions_mask)) {
242        return 0;
243    }
244
245    if (chmod(path, (int)mode)) return GB_IO_error("changing mode of", path);
246    return 0;
247}
248
249char *GB_follow_unix_link(const char *path) {   // returns the real path of a file
250    char buffer[1000];
251    char *path2;
252    char *pos;
253    char *res;
254    int len = readlink(path, buffer, 999);
255    if (len<0) return 0;
256    buffer[len] = 0;
257    if (path[0] == '/') return strdup(buffer);
258
259    path2 = strdup(path);
260    pos = strrchr(path2, '/');
261    if (!pos) {
262        free(path2);
263        return strdup(buffer);
264    }
265    *pos = 0;
266    res  = GBS_global_string_copy("%s/%s", path2, buffer);
267    free(path2);
268    return res;
269}
270
271GB_ERROR GB_rename_file(const char *oldpath, const char *newpath) {
272    long old_mod               = GB_mode_of_file(newpath); // keep filemode for existing files
273    if (old_mod == -1) old_mod = GB_mode_of_file(oldpath);
274
275    GB_ERROR error = NULL;
276    if (rename(oldpath, newpath) != 0) {
277        error = GB_IO_error("renaming", GBS_global_string("%s' into '%s", oldpath, newpath)); // Note: GB_IO_error quotes it's 2nd arg
278    }
279    else {
280        error = GB_set_mode_of_file(newpath, old_mod);
281    }
282   
283    sync();                                         // why ?
284    return error;
285}
286
287// --------------------------------------------------------------------------------
288
289#ifdef UNIT_TESTS
290#ifndef TEST_UNIT_H
291#include <test_unit.h>
292#endif
293
294void TEST_basic_file_checks() {
295    const char *someDir   = "general";
296    const char *someFile  = "general/text.input";
297    const char *noFile = "general/nosuch.input";
298
299    TEST_EXPECT_DIFFERENT(GB_mode_of_file(someFile), -1);
300    TEST_EXPECT_DIFFERENT(GB_mode_of_file(someDir), -1);
301    TEST_EXPECT_EQUAL(GB_mode_of_file(noFile), -1);
302    TEST_EXPECT_EQUAL(GB_mode_of_file(NULL), -1);
303
304    {
305        const char *linkToFile  = "fileLink";
306        const char *linkToDir   = "dirLink";
307        const char *linkNowhere = "brokenLink";
308
309        TEST_EXPECT_DIFFERENT(GB_unlink(linkToFile), -1);
310        TEST_EXPECT_DIFFERENT(GB_unlink(linkNowhere), -1);
311        TEST_EXPECT_DIFFERENT(GB_unlink(linkToDir), -1);
312
313        TEST_EXPECT_NO_ERROR(GB_symlink(someFile, linkToFile));
314        TEST_EXPECT_NO_ERROR(GB_symlink(someDir, linkToDir));
315        TEST_EXPECT_NO_ERROR(GB_symlink(noFile, linkNowhere));
316
317        TEST_EXPECT(GB_is_link(linkToFile));
318        TEST_EXPECT(GB_is_link(linkToDir));
319        TEST_EXPECT(GB_is_link(linkNowhere));
320        TEST_REJECT(GB_is_link(someFile));
321        TEST_REJECT(GB_is_link(noFile));
322        TEST_REJECT(GB_is_link(someDir));
323        TEST_REJECT(GB_is_link(NULL));
324
325        TEST_EXPECT(GB_is_regularfile(linkToFile));
326        TEST_REJECT(GB_is_regularfile(linkToDir));
327        TEST_REJECT(GB_is_regularfile(linkNowhere));
328        TEST_EXPECT(GB_is_regularfile(someFile));
329        TEST_REJECT(GB_is_regularfile(someDir));
330        TEST_REJECT(GB_is_regularfile(noFile));
331        TEST_REJECT(GB_is_regularfile(NULL));
332
333        TEST_REJECT(GB_is_directory(linkToFile));
334        TEST_EXPECT(GB_is_directory(linkToDir));
335        TEST_REJECT(GB_is_directory(linkNowhere));
336        TEST_REJECT(GB_is_directory(someFile));
337        TEST_REJECT(GB_is_directory(noFile));
338        TEST_EXPECT(GB_is_directory(someDir));
339        TEST_REJECT(GB_is_directory(NULL));
340
341        TEST_EXPECT(GB_is_readablefile(linkToFile));
342        TEST_REJECT(GB_is_readablefile(linkToDir));
343        TEST_REJECT(GB_is_readablefile(linkNowhere));
344        TEST_EXPECT(GB_is_readablefile(someFile));
345        TEST_REJECT(GB_is_readablefile(noFile));
346        TEST_REJECT(GB_is_readablefile(someDir));
347        TEST_REJECT(GB_is_readablefile(NULL));
348       
349        TEST_EXPECT(GB_is_readable(linkToDir));
350        TEST_EXPECT(GB_is_readable(someDir));
351
352        TEST_EXPECT_DIFFERENT(GB_mode_of_link(linkToFile), GB_mode_of_file(someFile));
353        TEST_EXPECT_DIFFERENT(GB_mode_of_link(linkToDir), GB_mode_of_file(someDir));
354        TEST_EXPECT_DIFFERENT(GB_mode_of_link(linkNowhere), -1);
355        TEST_EXPECT_EQUAL(GB_mode_of_link(NULL), -1);
356
357        TEST_EXPECT_DIFFERENT(GB_unlink(linkToFile), -1);
358        TEST_EXPECT_DIFFERENT(GB_unlink(linkToDir), -1);
359        TEST_EXPECT_DIFFERENT(GB_unlink(linkNowhere), -1);
360    }
361}
362
363#endif // UNIT_TESTS
364
365// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.