Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Even allocation of files among writable branches #89

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#define DEBUG_H

#include <errno.h>
#include <sys/time.h>
#include "opts.h"
#include "general.h"

#define DBG_IN() DBG("\n");

Expand All @@ -17,8 +19,12 @@
if (!uopt.debug) break; \
int _errno = errno; \
FILE* dbgfile = get_dbgfile(); \
fprintf(stderr, "%s(): %d: ", __func__, __LINE__); \
fprintf(dbgfile, "%s(): %d: ", __func__, __LINE__); \
struct timeval _tv_dbg; \
gettimeofday(&_tv_dbg, NULL); \
print_iso8601(stderr, _tv_dbg); \
print_iso8601(dbgfile, _tv_dbg); \
fprintf(stderr, " %s(): %d: ", __func__, __LINE__); \
fprintf(dbgfile, " %s(): %d: ", __func__, __LINE__); \
fprintf(stderr, format, ##__VA_ARGS__); \
fprintf(dbgfile, format, ##__VA_ARGS__); \
fflush(stderr); \
Expand Down
52 changes: 49 additions & 3 deletions src/findbranch.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <errno.h>

Expand Down Expand Up @@ -139,10 +140,10 @@ int __find_rw_branch_cutlast(const char *path, int rw_hint) {
if (branch < 0) goto out;

// Reminder rw_hint == -1 -> autodetect, we do not care which branch it is
if (uopt.branches[branch].rw
if (uopt.branches[branch].rw && !uopt.even
&& (rw_hint == -1 || branch == rw_hint)) goto out;

if (!uopt.cow_enabled) {
if (!(uopt.cow_enabled || uopt.even)) {
// So path exists, but is not writable.
branch = -1;
errno = EACCES;
Expand All @@ -152,7 +153,7 @@ int __find_rw_branch_cutlast(const char *path, int rw_hint) {
int branch_rw;
// since it is a directory, any rw-branch is fine
if (rw_hint == -1)
branch_rw = find_lowest_rw_branch(uopt.nbranches);
branch_rw = uopt.even ? find_evenly_rw_branch() : find_lowest_rw_branch(uopt.nbranches);
else
branch_rw = rw_hint;

Expand Down Expand Up @@ -239,3 +240,48 @@ int find_lowest_rw_branch(int branch_ro) {

RETURN(-1);
}

int find_evenly_rw_branch() {
DBG_IN();

int branch = -1;
uint64_t *spaces;
uint64_t choice;
uint64_t sum = 0;

spaces = malloc(uopt.nbranches * sizeof(*spaces));
if (!spaces)
goto out;

int i = 0;
for (i = 0; i < uopt.nbranches; i++) {
struct statvfs stb;
uint64_t free;
int res = statvfs_local(uopt.branches[i].path, &stb);
if (res == -1)
goto out;

free = uopt.branches[i].rw ? stb.f_frsize * stb.f_bavail : 0;
sum += free;
spaces[i] = sum;
DBG("branch = %d free = %"PRIu64" sum = %"PRIu64"\n", i, free,
sum);
}
if (sum == 0)
goto out;

choice = randupto64(sum - 1);
DBG("choice = %"PRIu64"\n", choice);

for (i = 0; i < uopt.nbranches; i++) {
if (spaces[i] > choice) {
branch = i;
break;
}
}

out:

free(spaces);
RETURN(branch);
}
1 change: 1 addition & 0 deletions src/findbranch.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ typedef enum searchflag {

int find_rorw_branch(const char *path);
int find_lowest_rw_branch(int branch_ro);
int find_evenly_rw_branch();
int find_rw_branch_cutlast(const char *path);
int __find_rw_branch_cutlast(const char *path, int rw_hint);
int find_rw_branch_cow(const char *path);
Expand Down
52 changes: 0 additions & 52 deletions src/fuse_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@
#include <sys/time.h>
#include <inttypes.h>

#ifdef linux
#include <sys/vfs.h>
#else
#include <sys/statvfs.h>
#endif

#ifdef HAVE_XATTR
#include <sys/xattr.h>
#endif
Expand Down Expand Up @@ -511,52 +505,6 @@ static int unionfs_rename(const char *from, const char *to) {
RETURN(0);
}

/**
* Wrapper function to convert the result of statfs() to statvfs()
* libfuse uses statvfs, since it conforms to POSIX. Unfortunately,
* glibc's statvfs parses /proc/mounts, which then results in reading
* the filesystem itself again - which would result in a deadlock.
* TODO: BSD/MacOSX
*/
static int statvfs_local(const char *path, struct statvfs *stbuf) {
#ifdef linux
/* glibc's statvfs walks /proc/mounts and stats entries found there
* in order to extract their mount flags, which may deadlock if they
* are mounted under the unionfs. As a result, we have to do this
* ourselves.
*/
struct statfs stfs;
int res = statfs(path, &stfs);
if (res == -1) RETURN(res);

memset(stbuf, 0, sizeof(*stbuf));
stbuf->f_bsize = stfs.f_bsize;
if (stfs.f_frsize) {
stbuf->f_frsize = stfs.f_frsize;
} else {
stbuf->f_frsize = stfs.f_bsize;
}
stbuf->f_blocks = stfs.f_blocks;
stbuf->f_bfree = stfs.f_bfree;
stbuf->f_bavail = stfs.f_bavail;
stbuf->f_files = stfs.f_files;
stbuf->f_ffree = stfs.f_ffree;
stbuf->f_favail = stfs.f_ffree; /* nobody knows */

/* We don't worry about flags, exactly because this would
* require reading /proc/mounts, and avoiding that and the
* resulting deadlocks is exactly what we're trying to avoid
* by doing this rather than using statvfs.
*/
stbuf->f_flag = 0;
stbuf->f_namemax = stfs.f_namelen;

RETURN(0);
#else
RETURN(statvfs(path, stbuf));
#endif
}

/**
* statvs implementation
*
Expand Down
69 changes: 69 additions & 0 deletions src/general.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
#include <pwd.h>
#include <grp.h>
#include <pthread.h>
#include <sys/time.h>

#ifdef linux
#include <sys/vfs.h>
#else
#include <sys/statvfs.h>
#endif

#include "unionfs.h"
#include "opts.h"
Expand Down Expand Up @@ -213,3 +220,65 @@ int set_owner(const char *path) {
}
RETURN(0);
}

void print_iso8601(FILE *file, struct timeval tv)
{
char timestampbuf[100];
struct tm tm;

localtime_r(&tv.tv_sec, &tm);
strftime(timestampbuf, sizeof("YYYY-MM-ddTHH:mm:ss.SSS+0000"), "%Y-%m-%dT%H:%M:%S.000%z", &tm);
sprintf(timestampbuf + 20, "%03ld%s", tv.tv_usec / 1000, timestampbuf + 23);
fprintf(file, timestampbuf);
}

uint64_t randupto64(uint64_t max)
{
return (uint64_t) (rand() / (double) ((uint64_t) RAND_MAX + 1) * (max + 1));
}

/**
* Wrapper function to convert the result of statfs() to statvfs()
* libfuse uses statvfs, since it conforms to POSIX. Unfortunately,
* glibc's statvfs parses /proc/mounts, which then results in reading
* the filesystem itself again - which would result in a deadlock.
* TODO: BSD/MacOSX
*/
int statvfs_local(const char *path, struct statvfs *stbuf) {
#ifdef linux
/* glibc's statvfs walks /proc/mounts and stats entries found there
* in order to extract their mount flags, which may deadlock if they
* are mounted under the unionfs. As a result, we have to do this
* ourselves.
*/
struct statfs stfs;
int res = statfs(path, &stfs);
if (res == -1) RETURN(res);

memset(stbuf, 0, sizeof(*stbuf));
stbuf->f_bsize = stfs.f_bsize;
if (stfs.f_frsize) {
stbuf->f_frsize = stfs.f_frsize;
} else {
stbuf->f_frsize = stfs.f_bsize;
}
stbuf->f_blocks = stfs.f_blocks;
stbuf->f_bfree = stfs.f_bfree;
stbuf->f_bavail = stfs.f_bavail;
stbuf->f_files = stfs.f_files;
stbuf->f_ffree = stfs.f_ffree;
stbuf->f_favail = stfs.f_ffree; /* nobody knows */

/* We don't worry about flags, exactly because this would
* require reading /proc/mounts, and avoiding that and the
* resulting deadlocks is exactly what we're trying to avoid
* by doing this rather than using statvfs.
*/
stbuf->f_flag = 0;
stbuf->f_namemax = stfs.f_namelen;

RETURN(0);
#else
RETURN(statvfs(path, stbuf));
#endif
}
3 changes: 3 additions & 0 deletions src/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ int hide_dir(const char *path, int branch_rw);
filetype_t path_is_dir (const char *path);
int maybe_whiteout(const char *path, int branch_rw, enum whiteout mode);
int set_owner(const char *path);
void print_iso8601(FILE *file, struct timeval tv);
uint64_t randupto64(uint64_t max);
int statvfs_local(const char *path, struct statvfs *stbuf);


#endif
6 changes: 6 additions & 0 deletions src/opts.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ static void print_help(const char *progname) {
" want to have a union of \"/\" \n"
" -o cow enable copy-on-write\n"
" mountpoint\n"
" -o even distribute file allocation evenly among\n"
" writable branches\n"
" the most free place\n"
" -o debug_file file to write debug information into\n"
" -o dirs=branch[=RO/RW][:branch...]\n"
" alternate way to specify directories to merge\n"
Expand Down Expand Up @@ -395,6 +398,9 @@ int unionfs_opt_proc(void *data, const char *arg, int key, struct fuse_args *out
case KEY_RELAXED_PERMISSIONS:
uopt.relaxed_permissions = true;
return 0;
case KEY_EVEN_PLACEMENT:
uopt.even = true;
return 0;
case KEY_VERSION:
printf("unionfs-fuse version: "VERSION"\n");
#ifdef HAVE_XATTR
Expand Down
2 changes: 2 additions & 0 deletions src/opts.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ typedef struct {
pthread_rwlock_t dbgpath_lock; // locks dbgpath
bool hide_meta_files;
bool relaxed_permissions;
bool even;

} uopt_t;

Expand All @@ -45,6 +46,7 @@ enum {
KEY_NOINITGROUPS,
KEY_RELAXED_PERMISSIONS,
KEY_STATFS_OMIT_RO,
KEY_EVEN_PLACEMENT,
KEY_VERSION
};

Expand Down
2 changes: 2 additions & 0 deletions src/unionfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
static struct fuse_opt unionfs_opts[] = {
FUSE_OPT_KEY("chroot=%s,", KEY_CHROOT),
FUSE_OPT_KEY("cow", KEY_COW),
FUSE_OPT_KEY("even", KEY_EVEN_PLACEMENT),
FUSE_OPT_KEY("debug_file=%s", KEY_DEBUG_FILE),
FUSE_OPT_KEY("dirs=%s", KEY_DIRS),
FUSE_OPT_KEY("--help", KEY_HELP),
Expand Down Expand Up @@ -88,6 +89,7 @@ int main(int argc, char *argv[]) {
#endif

umask(0);
srand(time(NULL));
int res = fuse_main(args.argc, args.argv, &unionfs_oper, NULL);
RETURN(uopt.doexit ? uopt.retval : res);
}