1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 
18 #if   defined(_WIN32) && !defined(__STDWX_H__)
19 #include "boinc_win.h"
20 #elif defined(_WIN32) && defined(__STDWX_H__)
21 #include "stdwx.h"
22 #define MAXPATHLEN 4096
23 #endif
24 
25 #if defined(__MINGW32__)
26 #include <fcntl.h>
27 #endif
28 
29 #if  defined(_MSC_VER) || defined(__MINGW32__)
30 #define getcwd    _getcwd
31 #define snprintf  _snprintf
32 #endif
33 
34 #if !defined(_WIN32) || defined(__CYGWIN32__)
35 #include "config.h"
36 #ifdef _USING_FCGI_
37 #include "boinc_fcgi.h"
38 #else
39 #include <cstdio>
40 #endif
41 #include <fcntl.h>
42 #include <cerrno>
43 #include <sys/stat.h>
44 #include <sys/file.h>
45 #include <cstring>
46 #include <cstdlib>
47 #include <sys/time.h>
48 #include <unistd.h>
49 #include <dirent.h>
50 
51 #if HAVE_SYS_RESOURCE_H
52 #include <sys/resource.h>
53 #endif
54 #if HAVE_SYS_MOUNT_H
55 #if HAVE_SYS_PARAM_H
56 #include <sys/param.h>
57 #endif
58 #include <sys/mount.h>
59 #endif
60 
61 #if HAVE_SYS_STATVFS_H
62 #include <sys/statvfs.h>
63 #define STATFS statvfs
64 #elif HAVE_SYS_STATFS_H
65 #include <sys/statfs.h>
66 #define STATFS statfs
67 #else
68 #define STATFS statfs
69 #endif
70 #endif
71 
72 #include "util.h"
73 #include "str_util.h"
74 #include "str_replace.h"
75 #include "error_numbers.h"
76 
77 #include "filesys.h"
78 
79 #ifdef __APPLE__
80 #include "mac_spawn.h"
81 #endif
82 
83 #ifdef _WIN32
84 typedef BOOL (CALLBACK* FreeFn)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
85 #endif
86 
87 using std::string;
88 
89 char boinc_failed_file[MAXPATHLEN];
90 
91 // routines for enumerating the entries in a directory
92 
is_file(const char * path)93 int is_file(const char* path) {
94 #ifdef _WIN32
95     DWORD dwAttrib = GetFileAttributesA(path);
96     return (dwAttrib != INVALID_FILE_ATTRIBUTES
97         && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
98     );
99 #else
100     struct stat sbuf;
101     int retval = lstat(path, &sbuf);
102     return (!retval && (((sbuf.st_mode) & S_IFMT) == S_IFREG));
103 #endif
104 }
105 
is_dir(const char * path)106 int is_dir(const char* path) {
107 #ifdef _WIN32
108     DWORD dwAttrib = GetFileAttributesA(path);
109     return (dwAttrib != INVALID_FILE_ATTRIBUTES
110         && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
111     );
112 #else
113     struct stat sbuf;
114     int retval = lstat(path, &sbuf);
115     return (!retval && (((sbuf.st_mode) & S_IFMT) == S_IFDIR));
116 #endif
117 }
118 
119 #ifndef _WIN32
120 
is_file_follow_symlinks(const char * path)121 int is_file_follow_symlinks(const char* path) {
122     struct stat sbuf;
123     int retval = stat(path, &sbuf);
124     return (!retval && (((sbuf.st_mode) & S_IFMT) == S_IFREG));
125 }
126 
is_dir_follow_symlinks(const char * path)127 int is_dir_follow_symlinks(const char* path) {
128     struct stat sbuf;
129     int retval = stat(path, &sbuf);
130     return (!retval && (((sbuf.st_mode) & S_IFMT) == S_IFDIR));
131 }
132 
is_symlink(const char * path)133 int is_symlink(const char* path) {
134     struct stat sbuf;
135     int retval = lstat(path, &sbuf);
136     return (!retval && S_ISLNK(sbuf.st_mode));
137 }
138 #endif
139 
140 // Open a directory
141 //
dir_open(const char * p)142 DIRREF dir_open(const char* p) {
143     DIRREF dirp;
144 #ifdef _WIN32
145     if (!is_dir(p)) return NULL;
146     dirp = (DIR_DESC*) calloc(sizeof(DIR_DESC), 1);
147     if (!dirp) {
148         fprintf(stderr, "calloc() failed in dir_open()\n");
149         return NULL;
150     }
151     dirp->first = true;
152     safe_strcpy(dirp->path, p);
153     safe_strcat(dirp->path, "\\*");
154     dirp->handle = INVALID_HANDLE_VALUE;
155 #else
156     dirp = opendir(p);
157     if (!dirp) return NULL;
158 #endif
159     return dirp;
160 }
161 
162 // Scan through a directory and return:
163 // 0 if an entry was found (the entry is returned in p)
164 // ERR_NOT_FOUND if we reached the end of the directory
165 // ERR_READDIR if some other error occurred
166 //
dir_scan(char * p,DIRREF dirp,int p_len)167 int dir_scan(char* p, DIRREF dirp, int p_len) {
168 #ifdef _WIN32
169     WIN32_FIND_DATAA data;
170     while (1) {
171         if (dirp->first) {
172             dirp->first = false;
173             dirp->handle = FindFirstFileA(dirp->path, &data);
174             if (dirp->handle == INVALID_HANDLE_VALUE) {
175                 return ERR_READDIR;
176             } else {
177                 // does Windows have "." and ".."?  well, just in case.
178                 //
179                 if (!strcmp(data.cFileName, ".")) continue;
180                 if (!strcmp(data.cFileName, "..")) continue;
181                 if (p) strlcpy(p, data.cFileName, p_len);
182                 return 0;
183             }
184         } else {
185             if (FindNextFileA(dirp->handle, &data)) {
186                 if (!strcmp(data.cFileName, ".")) continue;
187                 if (!strcmp(data.cFileName, "..")) continue;
188                 if (p) strlcpy(p, data.cFileName, p_len);
189                 return 0;
190             } else {
191                 DWORD ret = GetLastError();
192                 FindClose(dirp->handle);
193                 dirp->handle = INVALID_HANDLE_VALUE;
194                 if (ret == ERROR_NO_MORE_FILES) {
195                     return ERR_NOT_FOUND;
196                 }
197                 return ERR_READDIR;
198             }
199         }
200     }
201 #else
202     while (1) {
203         errno = 0;
204         dirent* dp = readdir(dirp);
205         if (dp) {
206             if (!strcmp(dp->d_name, ".")) continue;
207             if (!strcmp(dp->d_name, "..")) continue;
208             if (p) strlcpy(p, dp->d_name, p_len);
209             return 0;
210         } else {
211             if (errno) return ERR_READDIR;
212             return ERR_NOT_FOUND;
213         }
214     }
215 #endif
216 }
217 
218 // Close a directory
219 //
dir_close(DIRREF dirp)220 void dir_close(DIRREF dirp) {
221 #ifdef _WIN32
222     if (dirp->handle != INVALID_HANDLE_VALUE) {
223         FindClose(dirp->handle);
224         dirp->handle = INVALID_HANDLE_VALUE;
225     }
226     free(dirp);
227 #else
228     if (dirp) {
229         closedir(dirp);
230     }
231 #endif
232 }
233 
is_dir_empty(const char * p)234 bool is_dir_empty(const char *p) {
235     char file[MAXPATHLEN];
236 
237     DIRREF dir = dir_open(p);
238     if (!dir) return true;
239 
240     bool retval = (dir_scan(file, dir, sizeof(file)) != 0);
241     dir_close(dir);
242     return retval;
243 }
244 
DirScanner(string const & path)245 DirScanner::DirScanner(string const& path) {
246 #ifdef _WIN32
247     first = true;
248     handle = INVALID_HANDLE_VALUE;
249     if (!is_dir((char*)path.c_str())) {
250         return;
251     }
252     dir = path + "\\*";
253 #else
254     dirp = opendir(path.c_str());
255 #endif
256 }
257 
258 // Scan through a directory and return the next file name in it
259 //
scan(string & s)260 bool DirScanner::scan(string& s) {
261 #ifdef _WIN32
262     WIN32_FIND_DATAA data;
263     while (1) {
264         if (first) {
265             first = false;
266             handle = FindFirstFileA(dir.c_str(), &data);
267             if (handle == INVALID_HANDLE_VALUE) {
268                 return false;
269             } else {
270                 if (data.cFileName[0] == '.') continue;
271                 s = data.cFileName;
272                 return true;
273             }
274         } else {
275             if (FindNextFileA(handle, &data)) {
276                 if (data.cFileName[0] == '.') continue;
277                 s = data.cFileName;
278                 return true;
279             } else {
280                 FindClose(handle);
281                 handle = INVALID_HANDLE_VALUE;
282                 return false;
283             }
284         }
285     }
286 #else
287     if (!dirp) return false;
288 
289     while (1) {
290         dirent* dp = readdir(dirp);
291         if (dp) {
292             if (dp->d_name[0] == '.') continue;
293             s = dp->d_name;
294             return true;
295         } else {
296             return false;
297         }
298     }
299 #endif
300 }
301 
~DirScanner()302 DirScanner::~DirScanner() {
303 #ifdef _WIN32
304     if (handle != INVALID_HANDLE_VALUE) {
305         FindClose(handle);
306     }
307 #else
308     if (dirp) {
309         closedir(dirp);
310     }
311 #endif
312 }
313 
boinc_delete_file_aux(const char * path)314 static int boinc_delete_file_aux(const char* path) {
315 #ifdef _WIN32
316     if (!DeleteFileA(path)) {
317         return ERR_UNLINK;
318     }
319 #else
320     int retval = unlink(path);
321     if (retval) return ERR_UNLINK;
322 #endif
323     return 0;
324 }
325 
326 // Delete the file located at path
327 //
boinc_delete_file(const char * path)328 int boinc_delete_file(const char* path) {
329     int retval = 0;
330 
331     if (!boinc_file_exists(path)) {
332         return 0;
333     }
334     retval = boinc_delete_file_aux(path);
335     if (retval) {
336         double start = dtime();
337         do {
338             boinc_sleep(drand()*2);       // avoid lockstep
339             retval = boinc_delete_file_aux(path);
340             if (!retval) break;
341         } while (dtime() < start + FILE_RETRY_INTERVAL);
342     }
343     if (retval) {
344         safe_strcpy(boinc_failed_file, path);
345         return ERR_UNLINK;
346     }
347     return 0;
348 }
349 
350 // get file size
351 //
file_size(const char * path,double & size)352 int file_size(const char* path, double& size) {
353 #if defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__MINGW32__)
354     HANDLE h =  CreateFileA(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0);
355     if (h == INVALID_HANDLE_VALUE) return ERR_STAT;
356     LARGE_INTEGER lisize;
357     if (GetFileSizeEx(h, &lisize)) {
358         size = (double) lisize.QuadPart;
359         CloseHandle(h);
360         return 0;
361     }
362     return ERR_STAT;
363 #else
364     int retval;
365     struct stat sbuf;
366     retval = stat(path, &sbuf);
367     if (retval) return ERR_NOT_FOUND;
368     size = (double)sbuf.st_size;
369     return 0;
370 #endif
371 }
372 
boinc_truncate(const char * path,double size)373 int boinc_truncate(const char* path, double size) {
374     int retval;
375 #if defined(_WIN32) && !defined(__CYGWIN32__)
376     // the usual Windows nightmare.
377     // There's another function, SetEndOfFile(),
378     // that supposedly works with files over 2GB,
379     // but it uses HANDLES
380     //
381     int fd = _open(path, _O_RDWR, 0);
382     if (fd == -1) return ERR_TRUNCATE;
383     retval = _chsize(fd, (long)size);
384     _close(fd);
385 #else
386     retval = truncate(path, (off_t)size);
387 #endif
388     if (retval) return ERR_TRUNCATE;
389     return 0;
390 }
391 
392 // remove everything from specified directory
393 //
clean_out_dir(const char * dirpath)394 int clean_out_dir(const char* dirpath) {
395     char filename[MAXPATHLEN], path[MAXPATHLEN];
396     int retval;
397     DIRREF dirp;
398 
399     dirp = dir_open(dirpath);
400     if (!dirp) return 0;    // if dir doesn't exist, it's empty
401     while (1) {
402         safe_strcpy(filename, "");
403         retval = dir_scan(filename, dirp, sizeof(filename));
404         if (retval) break;
405 
406         snprintf(path, sizeof(path), "%s/%s", dirpath,  filename);
407         path[sizeof(path)-1] = 0;
408 
409         clean_out_dir(path);
410         boinc_rmdir(path);
411         retval = boinc_delete_file(path);
412         if (retval) {
413             dir_close(dirp);
414             return retval;
415         }
416     }
417     dir_close(dirp);
418     return 0;
419 }
420 
421 // return total size of files in directory and optionally its subdirectories
422 // Win: use special version because stat() is slow, can be avoided
423 // Unix: follow symbolic links
424 //
dir_size(const char * dirpath,double & size,bool recurse)425 int dir_size(const char* dirpath, double& size, bool recurse) {
426 #ifdef WIN32
427     char buf[_MAX_PATH];
428     char path2[_MAX_PATH];
429     double dsize = 0.0;
430     WIN32_FIND_DATAA findData;
431 
432     size = 0.0;
433     snprintf(path2, sizeof(path2), "%s/*", dirpath);
434     path2[sizeof(path2)-1] = 0;
435 
436     HANDLE hFind = ::FindFirstFileA(path2, &findData);
437     if (INVALID_HANDLE_VALUE == hFind) return ERR_OPENDIR;
438     do {
439         if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
440             if (!recurse) continue;
441             if (!strcmp(findData.cFileName, ".")) continue;
442             if (!strcmp(findData.cFileName, "..")) continue;
443 
444             dsize = 0.0;
445 
446             snprintf(buf, sizeof(buf), "%s/%s", dirpath, findData.cFileName);
447             buf[sizeof(buf)-1] = 0;
448 
449             dir_size(buf, dsize, true);
450             size += dsize;
451         } else {
452             size += findData.nFileSizeLow + ((__int64)(findData.nFileSizeHigh) << 32);
453         }
454     } while (FindNextFileA(hFind, &findData));
455 
456     ::FindClose(hFind);
457 #else
458     char filename[MAXPATHLEN], subdir[MAXPATHLEN];
459     int retval=0;
460     DIRREF dirp;
461     double x;
462 
463     size = 0.0;
464     dirp = dir_open(dirpath);
465     if (!dirp) return ERR_OPENDIR;
466     while (1) {
467         retval = dir_scan(filename, dirp, sizeof(filename));
468         if (retval) break;
469 
470         snprintf(subdir, sizeof(subdir), "%s/%s", dirpath, filename);
471         subdir[sizeof(subdir)-1] = 0;
472 
473         if (is_dir(subdir)) {
474             if (recurse) {
475                 retval = dir_size(subdir, x);
476                 if (retval) continue;
477                 size += x;
478             }
479         } else if (is_file(subdir)) {
480             retval = file_size(subdir, x);
481             if (retval) continue;
482             size += x;
483         }
484     }
485     dir_close(dirp);
486 #endif
487     return 0;
488 }
489 
boinc_fopen(const char * path,const char * mode)490 FILE* boinc_fopen(const char* path, const char* mode) {
491     // if opening for read, and file isn't there,
492     // leave now (avoid 5-second delay!!)
493     //
494     if (strchr(mode, 'r')) {
495         if (!boinc_file_exists(path)) {
496             return 0;
497         }
498     }
499 #ifndef _USING_FCGI_
500     FILE *f = fopen(path, mode);
501 #else
502     FCGI_FILE *f = FCGI::fopen(path,mode);
503 #endif
504 
505 #ifdef _WIN32
506     // on Windows: if fopen fails, try again for 5 seconds
507     // (since the file might be open by FastFind, Diskeeper etc.)
508     //
509     if (!f) {
510         double start = dtime();
511         do {
512             boinc_sleep(drand()*2);
513             f = _fsopen(path, mode, _SH_DENYNO);
514                 // _SH_DENYNO makes the file sharable while open
515             if (f) break;
516         } while (dtime() < start + FILE_RETRY_INTERVAL);
517     }
518 #else
519     // Unix - if call was interrupted, retry a few times
520     //
521     if (!f) {
522         for (int i=0; i<5; i++) {
523             boinc_sleep(drand());
524             if (errno != EINTR) break;
525 #ifndef _USING_FCGI_
526             f = fopen(path, mode);
527 #else
528             f = FCGI::fopen(path, mode);
529 #endif
530             if (f) break;
531         }
532     }
533     if (f) {
534         if (-1 == fcntl(fileno(f), F_SETFD, FD_CLOEXEC)) {
535             fclose(f);
536             return 0;
537         }
538     }
539 #endif
540     return f;
541 }
542 
543 // returns true if anything (file, dir, whatever) exists at given path;
544 // name is misleading.
545 //
boinc_file_exists(const char * path)546 int boinc_file_exists(const char* path) {
547 #ifdef _WIN32
548     // don't use _stat64 because it doesn't work with VS2015, XP client
549     DWORD dwAttrib = GetFileAttributesA(path);
550     return dwAttrib != INVALID_FILE_ATTRIBUTES;
551 #else
552     struct stat buf;
553     if (stat(path, &buf)) {
554         return false;     // stat() returns zero on success
555     }
556     return true;
557 #endif
558 }
559 
560 #if 0
561 // same, but doesn't traverse symlinks
562 //
563 int boinc_file_or_symlink_exists(const char* path) {
564 #ifdef _WIN32
565     return boinc_file_exists(path);
566 #else
567     struct stat buf;
568     if (lstat(path, &buf)) {
569         return false;     // stat() returns zero on success
570     }
571     return true;
572 #endif
573 }
574 #endif
575 
576 // returns zero on success, nonzero if didn't touch file
577 //
boinc_touch_file(const char * path)578 int boinc_touch_file(const char *path) {
579     if (boinc_file_exists(path)) {
580         return 0;
581     }
582 #ifndef _USING_FCGI_
583     FILE *fp = fopen(path, "w");
584 #else
585     FCGI_FILE *fp = FCGI::fopen(path, "w");
586 #endif
587     if (fp) {
588         fclose(fp);
589         return 0;
590     }
591     return -1;
592 }
593 
boinc_copy(const char * orig,const char * newf)594 int boinc_copy(const char* orig, const char* newf) {
595 #ifdef _WIN32
596     if (!CopyFileA(orig, newf, FALSE)) {     // FALSE means overwrite OK
597         return GetLastError();
598     }
599     return 0;
600 #elif defined(__EMX__)
601     char cmd[2*MAXPATHLEN];
602     snprintf(cmd, sizeof(cmd), "copy \"%s\" \"%s\"", orig, newf);
603     cmd[sizeof(cmd)-1] = 0;
604     return system(cmd);
605 #else
606     // POSIX requires that shells run from an application will use the
607     // real UID and GID if different from the effective UID and GID.
608     // Mac OS 10.4 did not enforce this, but OS 10.5 does.  Since
609     // system() invokes a shell, it may not properly copy the file's
610     // ownership or permissions when called from the BOINC Client
611     // under sandbox security, so we copy the file directly.
612     //
613     FILE *src, *dst;
614     int m, n;
615     int retval = 0;
616     unsigned char buf[65536];
617     src = boinc_fopen(orig, "r");
618     if (!src) return ERR_FOPEN;
619     dst = boinc_fopen(newf, "w");
620     if (!dst) {
621         fclose(src);
622         return ERR_FOPEN;
623     }
624     while (1) {
625         n = fread(buf, 1, sizeof(buf), src);
626         if (n <= 0) {
627             // could be either EOF or an error.
628             // Check for error case.
629             //
630             if (!feof(src)) {
631                 retval = ERR_FREAD;
632             }
633             break;
634         }
635         m = fwrite(buf, 1, n, dst);
636         if (m != n) {
637             retval = ERR_FWRITE;
638             break;
639         }
640     }
641     if (fclose(src)){
642        fclose(dst);
643        return ERR_FCLOSE;
644     }
645 
646     if (fclose(dst)){
647        return ERR_FCLOSE;
648     }
649     return retval;
650 #endif
651 }
652 
653 #ifndef _WIN32
654 // Copy file's ownership and permissions to the extent we are allowed
655 //
boinc_copy_attributes(const char * orig,const char * newf)656 int boinc_copy_attributes(const char* orig, const char* newf) {
657     struct stat sbuf;
658 
659     // Get source file's info
660     //
661     if (lstat(orig, &sbuf)) {
662         return ERR_STAT;
663     }
664     if (chmod(newf, sbuf.st_mode)) {
665         return ERR_CHMOD;
666     }
667     if (chown(newf, sbuf.st_uid, sbuf.st_gid)) {
668         return ERR_CHOWN;
669     }
670     return 0;
671 }
672 #endif
673 
boinc_rename_aux(const char * old,const char * newf)674 static int boinc_rename_aux(const char* old, const char* newf) {
675 #ifdef _WIN32
676     // MOVEFILE_COPY_ALLOWED is needed if destination is on another volume (move is simulated by copy&delete)
677     if (MoveFileExA(old, newf, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) return 0;
678     return GetLastError();
679 #else
680     // rename() doesn't work between filesystems.
681     // So if it fails, try the "mv" command, which does work
682     //
683     int retval = rename(old, newf);
684     if (retval) {
685         char buf[MAXPATHLEN+MAXPATHLEN];
686         sprintf(buf, "mv \"%s\" \"%s\"", old, newf);
687 #ifdef __APPLE__
688         // system() is deprecated in Mac OS 10.10.
689         // Apple says to call posix_spawn instead.
690         retval = callPosixSpawn(buf);
691 #else
692         retval = system(buf);
693 #endif
694     }
695     if (retval) return ERR_RENAME;
696     return 0;
697 #endif
698 }
699 
boinc_rename(const char * old,const char * newf)700 int boinc_rename(const char* old, const char* newf) {
701     int retval=0;
702 
703     retval = boinc_rename_aux(old, newf);
704     if (retval) {
705         // if the rename failed, and the file exists,
706         // retry a few times
707         //
708         if (!boinc_file_exists(old)) return ERR_FILE_MISSING;
709         double start = dtime();
710         do {
711             boinc_sleep(drand()*2);       // avoid lockstep
712             retval = boinc_rename_aux(old, newf);
713             if (!retval) break;
714         } while (dtime() < start + FILE_RETRY_INTERVAL);
715     }
716     return retval;
717 }
718 
719 // make a dir that's owner and group RWX
720 //
boinc_mkdir(const char * path)721 int boinc_mkdir(const char* path) {
722     if (is_dir(path)) return 0;
723 #ifdef _WIN32
724     if (!CreateDirectoryA(path, NULL)) {
725         return GetLastError();
726     }
727 #else
728     mode_t old_mask = umask(0);
729     int retval = mkdir(path, 0771);
730     umask(old_mask);
731     if (retval) return ERR_MKDIR;
732 #endif
733     return 0;
734 }
735 
boinc_rmdir(const char * name)736 int boinc_rmdir(const char* name) {
737 #ifdef _WIN32
738     if (!RemoveDirectoryA(name)) {
739         return ERR_RMDIR;
740     }
741 #else
742     int retval = rmdir(name);
743     if (retval) return ERR_RMDIR;
744 #endif
745     return 0;
746 }
747 
748 #ifndef _WIN32
boinc_chown(const char * path,gid_t gid)749 int boinc_chown(const char* path, gid_t gid) {
750     if (gid) {
751         if (chown(path, (uid_t)-1, gid)) {
752             return ERR_CHOWN;
753         }
754     }
755     return 0;
756 }
757 #endif
758 
759 // if "filepath" is of the form a/b/c,
760 // create directories dirpath/a, dirpath/a/b etc.
761 //
boinc_make_dirs(const char * dirpath,const char * filepath)762 int boinc_make_dirs(const char* dirpath, const char* filepath) {
763     char buf[MAXPATHLEN], oldpath[MAXPATHLEN], newpath[MAXPATHLEN];
764     int retval;
765     char *p, *q;
766 
767     if (strlen(filepath) + strlen(dirpath) > MAXPATHLEN-1) return ERR_BUFFER_OVERFLOW;
768     safe_strcpy(buf, filepath);
769     safe_strcpy(oldpath, dirpath);
770 
771     q = buf;
772     while(*q) {
773         p = strchr(q, '/');
774         if (!p) break;
775         *p = 0;
776         snprintf(newpath, sizeof(newpath), "%s/%s", oldpath, q);
777         newpath[sizeof(newpath)-1] = 0;
778         retval = boinc_mkdir(newpath);
779         if (retval) return retval;
780         safe_strcpy(oldpath, newpath);
781         q = p+1;
782     }
783     return 0;
784 }
785 
786 
FILE_LOCK()787 FILE_LOCK::FILE_LOCK() {
788 #if defined(_WIN32) && !defined(__CYGWIN32__)
789   handle = INVALID_HANDLE_VALUE;
790 #else
791     fd = -1;
792 #endif
793     locked = false;
794 }
795 
~FILE_LOCK()796 FILE_LOCK::~FILE_LOCK() {
797 #if !defined(_WIN32) || defined(__CYGWIN32__)
798     if (fd >= 0) close(fd);
799 #endif
800 }
801 
lock(const char * filename)802 int FILE_LOCK::lock(const char* filename) {
803 #if defined(_WIN32) && !defined(__CYGWIN32__)
804     handle = CreateFileA(
805         filename, GENERIC_WRITE,
806         0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
807     );
808     if (handle == INVALID_HANDLE_VALUE) {
809         return GetLastError();
810     }
811 #else
812     if (fd<0) {
813         fd = open(filename, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
814     }
815     if (fd<0) {
816         return ERR_OPEN;
817     }
818 
819     struct flock fl;
820     fl.l_type = F_WRLCK;
821     fl.l_whence = SEEK_SET;
822     fl.l_start = 0;
823     fl.l_len = 0;
824     if (fcntl(fd, F_SETLK, &fl) == -1) {
825         // ENOSYS means file locking is not implemented in this FS.
826         // In this case just return success (i.e. don't actually do locking)
827         //
828         if (errno != ENOSYS) {
829             return ERR_FCNTL;
830         }
831     }
832 #endif
833     locked = true;
834     return 0;
835 }
836 
unlock(const char * filename)837 int FILE_LOCK::unlock(const char* filename) {
838 #if defined(_WIN32) && !defined(__CYGWIN32__)
839     CloseHandle(handle);
840 #else
841     close(fd);
842     fd = -1;
843 #endif
844     boinc_delete_file(filename);
845     locked = false;
846     return 0;
847 }
848 
boinc_getcwd(char * path)849 void boinc_getcwd(char* path) {
850 #ifdef _WIN32
851     getcwd(path, MAXPATHLEN);
852 #else
853     char* p
854 #ifdef __GNUC__
855       __attribute__ ((unused))
856 #endif
857       = getcwd(path, MAXPATHLEN);
858 #endif
859 }
860 
relative_to_absolute(const char * relname,char * path)861 void relative_to_absolute(const char* relname, char* path) {
862     boinc_getcwd(path);
863     if (strlen(relname)) {
864         strcat(path, "/");
865         strcat(path, relname);
866     }
867 }
868 
869 
870 #if defined(_WIN32)
boinc_allocate_file(const char * path,double size)871 int boinc_allocate_file(const char* path, double size) {
872     int retval = 0;
873     HANDLE h = CreateFileA(
874         path,
875         GENERIC_WRITE,
876         0,
877         NULL,
878         OPEN_ALWAYS,
879         FILE_ATTRIBUTE_NORMAL,
880         NULL
881     );
882     if (h == INVALID_HANDLE_VALUE) return ERR_FOPEN;
883     LARGE_INTEGER sz;
884     sz.LowPart = fmod(size, 4294967296.);
885     sz.HighPart = (LONG)(size/4294967296.);
886     if (SetFilePointerEx(h, sz, NULL, FILE_BEGIN) == 0) {
887         retval = ERR_FOPEN;
888     }
889     if (!retval && SetEndOfFile(h) == 0) {
890         retval = ERR_FOPEN;
891     }
892     CloseHandle(h);
893     return retval;
894 }
895 
boinc_temp_file(const char * dir,const char * prefix,char * temp_path,double size)896 FILE* boinc_temp_file(
897     const char* dir, const char* prefix, char* temp_path, double size
898 ) {
899     GetTempFileNameA(dir, prefix, 0, temp_path);
900     boinc_allocate_file(temp_path, size);
901     return boinc_fopen(temp_path, "wb");
902 }
903 
904 #else
905 
906 // Unix version: use mkstemp.  tempnam() prioritizes an env var
907 // in deciding where to put temp file
908 
boinc_temp_file(const char * dir,const char * prefix,char * temp_path)909 FILE* boinc_temp_file(const char* dir, const char* prefix, char* temp_path) {
910     sprintf(temp_path, "%s/%s_XXXXXX", dir, prefix);
911     int fd = mkstemp(temp_path);
912     if (fd < 0) {
913         return 0;
914     }
915     return fdopen(fd, "wb");
916 }
917 
918 #endif
919 
boinc_path_to_dir(const char * path,char * dir)920 void boinc_path_to_dir(const char* path, char* dir) {
921     strcpy(dir, path);
922     char* p = strrchr(dir, '/');
923     if (p) {
924         *p = 0;
925     } else {
926         strcpy(dir, ".");
927     }
928 }
929 
930 // get total and free space on current filesystem (in bytes)
931 //
932 #ifdef _WIN32
get_filesystem_info(double & total_space,double & free_space,char *)933 int get_filesystem_info(double &total_space, double &free_space, char*) {
934     char cwd[MAXPATHLEN];
935     ULARGE_INTEGER TotalNumberOfFreeBytes;
936     ULARGE_INTEGER TotalNumberOfBytes;
937     ULARGE_INTEGER FreeBytesAvailable;
938     signed __int64 uMB;
939 
940     boinc_getcwd(cwd);
941     GetDiskFreeSpaceExA(
942         cwd,
943         &FreeBytesAvailable,
944         &TotalNumberOfBytes,
945         &TotalNumberOfFreeBytes
946     );
947 
948     uMB = FreeBytesAvailable.QuadPart / (1024 * 1024);
949     free_space = uMB * 1024.0 * 1024.0;
950     uMB = TotalNumberOfBytes.QuadPart / (1024 * 1024);
951     total_space = uMB * 1024.0 * 1024.0;
952 
953 #else
954 int get_filesystem_info(double &total_space, double &free_space, char* path) {
955 #ifdef STATFS
956     struct STATFS fs_info;
957 
958     int retval = STATFS(path, &fs_info);
959     if (retval) {
960 #ifndef _USING_FCGI_
961         perror("statvfs");
962 #else
963         FCGI::perror("statvfs");
964 #endif
965         return ERR_STATFS;
966     }
967 #if HAVE_SYS_STATVFS_H
968     total_space = (double)fs_info.f_frsize * (double)fs_info.f_blocks;
969     free_space = (double)fs_info.f_frsize * (double)fs_info.f_bavail;
970 #else
971     total_space = (double)fs_info.f_bsize * (double)fs_info.f_blocks;
972     free_space = (double)fs_info.f_bsize * (double)fs_info.f_bavail;
973 #endif
974 #else
975 #error Need to specify a method to obtain free/total disk space
976 #endif
977 #endif
978     return 0;
979 }
980