source: branches/profile/CORE/arb_file.cxx

Last change on this file was 12438, checked in by westram, 10 years ago
  • activate errors on missing test-source location
    • publish all missing tests
    • missing location apparently only occurs
      • in dynamic libraries and
      • for last test of a module Note: the reverse is not true!
File size: 10.4 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    if (chmod(path, (int)mode)) return GBS_global_string("Cannot change mode of '%s'", path);
220    return 0;
221}
222
223char *GB_follow_unix_link(const char *path) {   // returns the real path of a file
224    char buffer[1000];
225    char *path2;
226    char *pos;
227    char *res;
228    int len = readlink(path, buffer, 999);
229    if (len<0) return 0;
230    buffer[len] = 0;
231    if (path[0] == '/') return strdup(buffer);
232
233    path2 = strdup(path);
234    pos = strrchr(path2, '/');
235    if (!pos) {
236        free(path2);
237        return strdup(buffer);
238    }
239    *pos = 0;
240    res  = GBS_global_string_copy("%s/%s", path2, buffer);
241    free(path2);
242    return res;
243}
244
245GB_ERROR GB_rename_file(const char *oldpath, const char *newpath) {
246    long old_mod               = GB_mode_of_file(newpath); // keep filemode for existing files
247    if (old_mod == -1) old_mod = GB_mode_of_file(oldpath);
248
249    GB_ERROR error = NULL;
250    if (rename(oldpath, newpath) != 0) {
251        error = GB_IO_error("renaming", GBS_global_string("%s' into '%s", oldpath, newpath)); // Note: GB_IO_error quotes it's 2nd arg
252    }
253    else {
254        error = GB_set_mode_of_file(newpath, old_mod);
255    }
256   
257    sync();                                         // why ?
258    return error;
259}
260
261// --------------------------------------------------------------------------------
262
263#ifdef UNIT_TESTS
264#ifndef TEST_UNIT_H
265#include <test_unit.h>
266#endif
267
268void TEST_basic_file_checks() {
269    const char *someDir   = "general";
270    const char *someFile  = "general/text.input";
271    const char *noFile = "general/nosuch.input";
272
273    TEST_EXPECT_DIFFERENT(GB_mode_of_file(someFile), -1);
274    TEST_EXPECT_DIFFERENT(GB_mode_of_file(someDir), -1);
275    TEST_EXPECT_EQUAL(GB_mode_of_file(noFile), -1);
276    TEST_EXPECT_EQUAL(GB_mode_of_file(NULL), -1);
277
278    {
279        const char *linkToFile  = "fileLink";
280        const char *linkToDir   = "dirLink";
281        const char *linkNowhere = "brokenLink";
282
283        TEST_EXPECT_DIFFERENT(GB_unlink(linkToFile), -1);
284        TEST_EXPECT_DIFFERENT(GB_unlink(linkNowhere), -1);
285        TEST_EXPECT_DIFFERENT(GB_unlink(linkToDir), -1);
286
287        TEST_EXPECT_NO_ERROR(GB_symlink(someFile, linkToFile));
288        TEST_EXPECT_NO_ERROR(GB_symlink(someDir, linkToDir));
289        TEST_EXPECT_NO_ERROR(GB_symlink(noFile, linkNowhere));
290
291        TEST_EXPECT(GB_is_link(linkToFile));
292        TEST_EXPECT(GB_is_link(linkToDir));
293        TEST_EXPECT(GB_is_link(linkNowhere));
294        TEST_REJECT(GB_is_link(someFile));
295        TEST_REJECT(GB_is_link(noFile));
296        TEST_REJECT(GB_is_link(someDir));
297        TEST_REJECT(GB_is_link(NULL));
298
299        TEST_EXPECT(GB_is_regularfile(linkToFile));
300        TEST_REJECT(GB_is_regularfile(linkToDir));
301        TEST_REJECT(GB_is_regularfile(linkNowhere));
302        TEST_EXPECT(GB_is_regularfile(someFile));
303        TEST_REJECT(GB_is_regularfile(someDir));
304        TEST_REJECT(GB_is_regularfile(noFile));
305        TEST_REJECT(GB_is_regularfile(NULL));
306
307        TEST_REJECT(GB_is_directory(linkToFile));
308        TEST_EXPECT(GB_is_directory(linkToDir));
309        TEST_REJECT(GB_is_directory(linkNowhere));
310        TEST_REJECT(GB_is_directory(someFile));
311        TEST_REJECT(GB_is_directory(noFile));
312        TEST_EXPECT(GB_is_directory(someDir));
313        TEST_REJECT(GB_is_directory(NULL));
314
315        TEST_EXPECT(GB_is_readablefile(linkToFile));
316        TEST_REJECT(GB_is_readablefile(linkToDir));
317        TEST_REJECT(GB_is_readablefile(linkNowhere));
318        TEST_EXPECT(GB_is_readablefile(someFile));
319        TEST_REJECT(GB_is_readablefile(noFile));
320        TEST_REJECT(GB_is_readablefile(someDir));
321        TEST_REJECT(GB_is_readablefile(NULL));
322       
323        TEST_EXPECT(GB_is_readable(linkToDir));
324        TEST_EXPECT(GB_is_readable(someDir));
325
326        TEST_EXPECT_DIFFERENT(GB_mode_of_link(linkToFile), GB_mode_of_file(someFile));
327        TEST_EXPECT_DIFFERENT(GB_mode_of_link(linkToDir), GB_mode_of_file(someDir));
328        TEST_EXPECT_DIFFERENT(GB_mode_of_link(linkNowhere), -1);
329        TEST_EXPECT_EQUAL(GB_mode_of_link(NULL), -1);
330
331        TEST_EXPECT_DIFFERENT(GB_unlink(linkToFile), -1);
332        TEST_EXPECT_DIFFERENT(GB_unlink(linkToDir), -1);
333        TEST_EXPECT_DIFFERENT(GB_unlink(linkNowhere), -1);
334    }
335}
336TEST_PUBLISH(TEST_basic_file_checks);
337
338#endif // UNIT_TESTS
339
340// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.