Client: measure disk usage in terms of allocated disk space, not file size.

On filesystems that use compression, the disk space allocated to a file
is generally less than the (logical) size of the file.

In this case, the client can fail to get work
because it thinks there's insufficient disk space.

Fix: change things so that the client measures disk usage
(per-project and total) in terms of allocated space rather than file size.

But still use logical file sizes in two places:
- in checking that downloaded files are the right size
- in calculating a job's disk usage to see if it exceeds
the project-specified limit.

New functions: file_size_alloc() and dir_size_alloc().
These are like file_size() and dir_size() except they return
allocated rather than logical size.
This commit is contained in:
David Anderson 2020-05-11 20:02:47 -07:00
parent db94a50066
commit a985959da5
5 changed files with 115 additions and 10 deletions

View File

@ -78,6 +78,7 @@ struct ACTIVE_TASK {
double peak_working_set_size;
double peak_swap_size;
double peak_disk_usage;
// based on real (not allocated/compressed) file sizes
// START OF ITEMS ALSO SAVED IN CLIENT STATE FILE
@ -204,7 +205,9 @@ struct ACTIVE_TASK {
void cleanup_task();
int current_disk_usage(double&);
// disk used by output files and temp files of this task
// total sizes of output files and temp files of this task
// This is compared with project-specified limits
// to decide whether to abort job; no other use.
int get_free_slot(RESULT*);
int start(bool test=false); // start a process

View File

@ -90,14 +90,14 @@ int CLIENT_STATE::get_disk_usages() {
for (i=0; i<projects.size(); i++) {
p = projects[i];
p->disk_usage = 0;
retval = dir_size(p->project_dir(), size);
retval = dir_size_alloc(p->project_dir(), size);
if (!retval) p->disk_usage = size;
}
for (i=0; i<active_tasks.active_tasks.size(); i++) {
ACTIVE_TASK* atp = active_tasks.active_tasks[i];
get_slot_dir(atp->slot, buf, sizeof(buf));
retval = dir_size(buf, size);
retval = dir_size_alloc(buf, size);
if (retval) continue;
atp->wup->project->disk_usage += size;
}
@ -105,7 +105,7 @@ int CLIENT_STATE::get_disk_usages() {
p = projects[i];
total_disk_usage += p->disk_usage;
}
retval = dir_size(".", size, false);
retval = dir_size_alloc(".", size, false);
if (!retval) {
client_disk_usage = size;
total_disk_usage += size;

View File

@ -168,8 +168,8 @@ static void handle_get_disk_usage(GUI_RPC_CONN& grc) {
);
}
dir_size(".", boinc_non_project, false);
dir_size("locale", size, false);
dir_size_alloc(".", boinc_non_project, false);
dir_size_alloc("locale", size, false);
boinc_non_project += size;
#ifdef __APPLE__
if (gstate.launched_by_manager) {
@ -179,9 +179,13 @@ static void handle_get_disk_usage(GUI_RPC_CONN& grc) {
OSStatus err = noErr;
retval = proc_pidpath(getppid(), path, sizeof(path));
if (retval <= 0) err = fnfErr;
if (! err) dir_size(path, manager_size, true);
if (! err) boinc_non_project += manager_size;
if (retval <= 0) {
err = fnfErr;
}
if (!err) {
dir_size_alloc(path, manager_size, true);
boinc_non_project += manager_size;
}
}
#endif
boinc_total = boinc_non_project;

View File

@ -370,6 +370,32 @@ int file_size(const char* path, double& size) {
#endif
}
// get file allocation size, i.e. how much disk space does it use.
// This can be less than the file size on compressed filesystems,
// or if the file has holes.
// It can also be slightly more.
//
int file_size_alloc(const char* path, double& size) {
#if defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__MINGW32__)
HANDLE h = CreateFileA(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0, OPEN_EXISTING, 0, 0);
if (h == INVALID_HANDLE_VALUE) return ERR_STAT;
LARGE_INTEGER lisize;
if (GetCompressedFileSizeEx(h, &lisize)) {
size = (double) lisize.QuadPart;
CloseHandle(h);
return 0;
}
return ERR_STAT;
#else
int retval;
struct stat sbuf;
retval = stat(path, &sbuf);
if (retval) return ERR_NOT_FOUND;
size = ((double)sbuf.st_blocks)*512.;
return 0;
#endif
}
int boinc_truncate(const char* path, double size) {
int retval;
#if defined(_WIN32) && !defined(__CYGWIN32__)
@ -487,6 +513,76 @@ int dir_size(const char* dirpath, double& size, bool recurse) {
return 0;
}
// return total allocated size of files in directory and optionally its subdirectories
// Win: use special version because stat() is slow, can be avoided
// Unix: follow symbolic links
//
int dir_size_alloc(const char* dirpath, double& size, bool recurse) {
#ifdef WIN32
char buf[_MAX_PATH];
char path2[_MAX_PATH];
double dsize = 0.0;
WIN32_FIND_DATAA findData;
size = 0.0;
snprintf(path2, sizeof(path2), "%s/*", dirpath);
path2[sizeof(path2)-1] = 0;
HANDLE hFind = ::FindFirstFileA(path2, &findData);
if (INVALID_HANDLE_VALUE == hFind) return ERR_OPENDIR;
do {
snprintf(buf, sizeof(buf), "%.*s/%.*s", DIR_LEN, dirpath, FILE_LEN, findData.cFileName);
buf[sizeof(buf)-1] = 0;
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!recurse) continue;
if (!strcmp(findData.cFileName, ".")) continue;
if (!strcmp(findData.cFileName, "..")) continue;
dsize = 0.0;
dir_size_alloc(buf, dsize, true);
size += dsize;
} else {
double s;
if (file_size_alloc(buf, s) == 0) {
size += s;
}
}
} while (FindNextFileA(hFind, &findData));
::FindClose(hFind);
#else
char filename[MAXPATHLEN], subdir[MAXPATHLEN];
int retval=0;
DIRREF dirp;
double x;
size = 0.0;
dirp = dir_open(dirpath);
if (!dirp) return ERR_OPENDIR;
while (1) {
retval = dir_scan(filename, dirp, sizeof(filename));
if (retval) break;
snprintf(subdir, sizeof(subdir), "%.*s/%.*s", DIR_LEN, dirpath, FILE_LEN, filename);
subdir[sizeof(subdir)-1] = 0;
if (is_dir(subdir)) {
if (recurse) {
retval = dir_size_alloc(subdir, x);
if (retval) continue;
size += x;
}
} else if (is_file(subdir)) {
retval = file_size_alloc(subdir, x);
if (retval) continue;
size += x;
}
}
dir_close(dirp);
#endif
return 0;
}
FILE* boinc_fopen(const char* path, const char* mode) {
// if opening for read, and file isn't there,
// leave now (avoid 5-second delay!!)

View File

@ -95,8 +95,10 @@ extern "C" {
#ifdef __cplusplus
extern int file_size(const char*, double&);
extern int clean_out_dir(const char*);
extern int file_size_alloc(const char*, double&);
extern int dir_size(const char* dirpath, double&, bool recurse=true);
extern int dir_size_alloc(const char* dirpath, double&, bool recurse=true);
extern int clean_out_dir(const char*);
extern int get_filesystem_info(double& total, double& free, char* path=const_cast<char *>("."));
extern bool is_path_absolute(const std::string path);