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