source: tags/ms_r16q3/CORE/arb_file.cxx

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