diff --git a/ChangeLog b/ChangeLog index fbc1ee3..7435b6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,36 @@ ChangeLog for davfs2 -------------------- +2009-05-31 Werner Baumann (werner.baumann@onlinehome.de) + * cache.c, cache.h, dav_coda.c, dav_fuse.c: + Cache quota in global variable fs_stat. + Update with dir_refresh and when closing or + deleting files. + Function dav_stafs now returns a pointer. + +2009-05-30 Werner Baumann (werner.baumann@onlinehome.de) + * webdav.c, webdav.h, dav_quota: + Return total webspace instead of available. + Rember support for quota in static flag use_rfc. + Add support for method USERINFO. + * webdav.c: + Rename block_writer into file_reader for consistency + with neon naming conventions. + * cache.c, dav_open, dav_write: + Open directories O_RDWR again, but prevent writing + by applications. + +2009-05-29 Werner Baumann (werner.baumann@onlinehome.de) + * mount_davfs.c, check_double_mounts: + Free temporary string mp. + * cache.c, parse_index: + Free index. + * webdav.c, dav_init_webdav: + Make custom_header a global variable to not disturb + code test tools. + * webdav.c, dav_quota: + Initialize ctx to 0; add ctx.error, use strtoull. + 2009-05-25 Werner Baumann (werner.baumann@onlinehome.de) * cache.c, mount_davfs.c: Add missing includes. diff --git a/src/cache.c b/src/cache.c index 85a4bfd..7352cf3 100644 --- a/src/cache.c +++ b/src/cache.c @@ -128,8 +128,8 @@ static const char* const type[] = { /* Private global variables */ /*==========================*/ -/* Number of nodes. */ -static int nnodes; +/* File system statistics. */ +static dav_stat *fs_stat; /* Root node of the directory cache. */ static dav_node *root; @@ -316,6 +316,15 @@ update_node(dav_node *node, dav_props *props); static void update_path(dav_node *node, const char *src_path, const char *dst_path); +static inline void +update_stat(off_t size, int sign) +{ + if (!fs_stat->utime) return; + fs_stat->bfree += sign * (size / fs_stat->bsize); + fs_stat->bavail = fs_stat->bfree; + fs_stat->ffree = fs_stat->bfree; +} + /* Get information about node. */ static int @@ -580,6 +589,9 @@ dav_init_cache(const dav_args *args, const char *mpoint) max_retry = args->max_retry; lock_refresh = args->lock_refresh; + fs_stat = (dav_stat *) malloc(sizeof(dav_stat)); + if (!fs_stat) abort(); + if (debug) syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "Checking cache directory"); max_cache_size = args->cache_size * 0x100000; @@ -602,6 +614,19 @@ dav_init_cache(const dav_args *args, const char *mpoint) clean_cache(); + fs_stat->blocks = 0x65B9AA; + fs_stat->bfree = 0x32DCD5; + fs_stat->bavail = 0x32DCD5; + fs_stat->ffree = 666666; + struct stat cache_st; + if (stat(cache_dir, &cache_st) == 0) { + fs_stat->bsize = cache_st.st_blksize; + } else { + fs_stat->bsize = 4096; + } + fs_stat->namelen = 256; + fs_stat->utime = 0; + int ret = update_directory(root, 0); if (ret == EAGAIN) { root->utime = 0; @@ -621,6 +646,8 @@ dav_init_cache(const dav_args *args, const char *mpoint) } else if (ret) { error(EXIT_FAILURE, 0, _("Mounting failed.\n%s"), dav_get_webdav_error()); + } else { + dav_statfs(); } } @@ -681,14 +708,8 @@ dav_register_kernel_interface(dav_write_dir_entry_fn write_fn, int *flush_flag, if (flush_flag) flush = flush_flag; - if (blksize) { - struct stat st; - if (stat(cache_dir, &st) == 0) { - *blksize = st.st_blksize; - } else { - *blksize = 4096; - } - } + if (blksize) + *blksize = fs_stat->bsize; return alignment; } @@ -699,7 +720,7 @@ dav_tidy_cache(void) { if (debug) { syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), - "tidy: %i of %i nodes changed", nchanged, nnodes); + "tidy: %i of %llu nodes changed", nchanged, fs_stat->files); syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "cache-size: %llu MiBytes.", (cache_size + 0x80000) / 0x100000); } @@ -812,8 +833,10 @@ dav_close(dav_node *node, int fd, int flags, pid_t pid, pid_t pgid) return 0; } + update_stat(node->size, 1); attr_from_cache_file(node); set_upload_time(node); + update_stat(node->size, -1); if (delay_upload == 0 && (is_dirty(node) || is_created(node)) && !is_open_write(node) && !is_backup(node)) { @@ -1095,7 +1118,7 @@ dav_open(int *fd, dav_node *node, int flags, pid_t pid, pid_t pgid, uid_t uid, if (create_dir_cache_file(node) != 0) return EIO; node->atime = time(NULL); - return open_file(fd, node, O_RDONLY, pid, pgid, uid); + return open_file(fd, node, O_RDWR, pid, pgid, uid); } int ret = 0; @@ -1202,6 +1225,7 @@ dav_remove(dav_node *parent, const char *name, uid_t uid) if (ret) return ret; + update_stat(node->size, 1); remove_from_tree(node); remove_from_changed(node); if (is_open(node)) { @@ -1486,32 +1510,22 @@ dav_setattr(dav_node *node, uid_t uid, int sm, mode_t mode, int so, } -dav_stat +dav_stat * dav_statfs(void) { - dav_stat st; - st.blocks = 0x65B9AA; - st.bfree = 0x32DCD5; - st.bavail = 0x32DCD5; - st.files = nnodes; - st.ffree = 666666; - struct stat cache_st; - if (stat(cache_dir, &cache_st) == 0) { - st.bsize = cache_st.st_blksize; - } else { - st.bsize = 4096; - } - st.namelen = 256; - - off_t used = 0; - if (dav_quota(root->path, &st.blocks, &used) == 0) { - st.blocks /= st.bsize; - st.bfree = st.blocks - (used / st.bsize) - 1; - st.bavail = st.bfree; - st.ffree = st.bfree; + if (time(NULL) > (fs_stat->utime + dir_refresh)) { + off64_t total = 0; + off64_t used = 0; + if (dav_quota(root->path, &total, &used) == 0) { + fs_stat->blocks = total / fs_stat->bsize; + fs_stat->bfree = fs_stat->blocks - (used / fs_stat->bsize) - 1; + fs_stat->bavail = fs_stat->bfree; + fs_stat->ffree = fs_stat->bfree; + fs_stat->utime = time(NULL); + } } - return st; + return fs_stat; } @@ -1538,6 +1552,8 @@ dav_write(size_t *written, dav_node * node, int fd, char *buf, size_t size, { if (!exists(node)) return ENOENT; + if (is_dir(node)) + return EBADF; dav_handle *fh = get_file_handle(node, fd, 0, 0, 0); if (!fh) @@ -1770,7 +1786,7 @@ delete_node(dav_node *node) free(tofree); } free(node); - nnodes--; + fs_stat->files--; } @@ -2016,7 +2032,7 @@ new_node(dav_node *parent, mode_t mode) if (debug) syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "new node: %p->%p", node->parent, node); - nnodes++; + fs_stat->files++; return node; } @@ -2824,8 +2840,10 @@ parse_index(void) { char *index = ne_concat(cache_dir, "/", DAV_INDEX, NULL); FILE *idx = fopen(index, "r"); - if (!idx) + if (!idx) { + free(index); return; + } char *buf = ne_malloc(DAV_XML_BUF_SIZE); size_t len = fread(buf, 1, DAV_XML_BUF_SIZE, idx); diff --git a/src/cache.h b/src/cache.h index cfa1f10..a95f252 100644 --- a/src/cache.h +++ b/src/cache.h @@ -147,13 +147,17 @@ struct dav_node_item { /* Returned by dav_statfs(). */ typedef struct dav_stat dav_stat; struct dav_stat { - off_t blocks; - off_t bfree; - off_t bavail; - off_t files; - off_t ffree; - off_t bsize; - off_t namelen; + off64_t blocks; + off64_t bfree; + off64_t bavail; + off64_t files; + off64_t ffree; + off_t bsize; + off_t namelen; + time_t utime; /* Time when last updated with data from the server. + must When 0 this structure contains fake data, + that not be changed when a file is changed or + deleted. */ }; @@ -409,9 +413,12 @@ dav_setattr(dav_node *node, uid_t uid, int sm, mode_t mode, int so, uid_t owner, int sg, gid_t gid, int sat, time_t atime, int smt, time_t mtime, int ssz, off_t size); -/* Returns struct dav_stat which currently is mainly fake. + +/* Returns struct dav_stat. If the server does not provide theinformation + it will contain fake data. No permissions necessary. */ -dav_stat dav_statfs(void); +dav_stat * +dav_statfs(void); /* Calls fsync() for all filedescriptors of node, that are not read only. @@ -424,7 +431,6 @@ dav_sync(dav_node *node); offset. The number of bytes written is returned in len. The file must be opened writeonly or readwrite. */ -/* TODO: Remove pid; check flags. */ int dav_write(size_t *written, dav_node * node, int fd, char *buf, size_t size, off_t offset); diff --git a/src/dav_coda.c b/src/dav_coda.c index 36d02c3..e30cae8 100644 --- a/src/dav_coda.c +++ b/src/dav_coda.c @@ -684,13 +684,17 @@ coda_statfs(void) if (debug) syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "CODA_STATFS:"); - dav_stat st = dav_statfs(); + dav_stat *st = dav_statfs(); + if (!st) { + oh->result = EIO; + return sizeof(struct coda_out_hdr); + } - out->stat.f_blocks = st.blocks; - out->stat.f_bfree = st.bfree; - out->stat.f_bavail = st.bavail; - out->stat.f_files = st.files; - out->stat.f_ffree = st.ffree; + out->stat.f_blocks = st->blocks; + out->stat.f_bfree = st->bfree; + out->stat.f_bavail = st->bavail; + out->stat.f_files = st->files; + out->stat.f_ffree = st->ffree; oh->result = 0; return sizeof(struct coda_statfs_out); diff --git a/src/dav_fuse.c b/src/dav_fuse.c index 4295581..8a82920 100644 --- a/src/dav_fuse.c +++ b/src/dav_fuse.c @@ -972,16 +972,20 @@ fuse_stat(void) if (debug) syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_STATFS:"); - dav_stat st = dav_statfs(); + dav_stat *st = dav_statfs(); + if (!st) { + oh->error = -EIO; + return sizeof(struct fuse_out_header); + } - out->st.blocks = st.blocks; - out->st.bfree = st.bfree; - out->st.bavail = st.bavail; - out->st.files = st.files; - out->st.ffree = st.ffree; + out->st.blocks = st->blocks; + out->st.bfree = st->bfree; + out->st.bavail = st->bavail; + out->st.files = st->files; + out->st.ffree = st->ffree; out->st.bsize = (buf_size - sizeof(struct fuse_in_header) - sizeof(struct fuse_write_in) - 4095) & ~4095; - out->st.namelen = st.namelen; + out->st.namelen = st->namelen; out->st.frsize = 0; out->st.padding = 0; int i; diff --git a/src/mount_davfs.c b/src/mount_davfs.c index 5c3c407..7982c0b 100644 --- a/src/mount_davfs.c +++ b/src/mount_davfs.c @@ -654,8 +654,8 @@ check_double_mounts(dav_args *args) m = strchr(mp, '/'); } char *pidf = NULL; - if (asprintf(&pidf, "%s/%s.pid", DAV_SYS_RUN, mp) < 0) - abort(); + if (asprintf(&pidf, "%s/%s.pid", DAV_SYS_RUN, mp) < 0) abort(); + free(mp); if (args->debug & DAV_DBG_CONFIG) syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "PID file: %s", pidf); diff --git a/src/webdav.c b/src/webdav.c index c324aaf..888e197 100644 --- a/src/webdav.c +++ b/src/webdav.c @@ -90,8 +90,9 @@ typedef struct { } get_context; typedef struct { - off_t available; /* Available bytes. */ - off_t used; /* Used bytes. */ + int error; + off64_t total; /* Total amount of available bytes. */ + off64_t used; /* Used bytes. */ } quota_context; @@ -203,6 +204,9 @@ static iconv_t to_server_enc; /* A GNU custom stream, used to redirect neon debug messages to syslog. */ static FILE *log_stream; +/* A user defined header that is added to all requests. */ +char *custom_header; + #if NE_VERSION_MINOR > 25 /* Session cookie. */ static char *cookie; @@ -248,7 +252,7 @@ static int auth(void *userdata, const char *realm, int attempt, char *user, char *pwd); static int -block_writer(void *userdata, const char *block, size_t length); +file_reader(void *userdata, const char *block, size_t length); #if NE_VERSION_MINOR < 26 @@ -276,6 +280,9 @@ quota_result(void *userdata, const ne_uri *uri, const ne_prop_result_set *set); #endif /* NE_VERSION_MINOR >= 26 */ +static int +quota_reader(void *userdata, const char *block, size_t length); + static int ssl_verify(void *userdata, int failures, const ne_ssl_certificate *cert); @@ -429,7 +436,7 @@ dav_init_webdav(const dav_args *args) } if (args->header) { - char *custom_header = ne_strdup(args->header); + custom_header = ne_strdup(args->header); ne_hook_pre_send(session, add_header, custom_header); } @@ -674,7 +681,7 @@ dav_get_file(const char *path, const char *cache_path, off_t *size, ne_add_request_header(req, "If-Modified-Since", mod_time); } - ne_add_response_body_reader(req, ne_accept_2xx, block_writer, &ctx); + ne_add_response_body_reader(req, ne_accept_2xx, file_reader, &ctx); ret = ne_request_dispatch(req); ret = get_error(ret, "GET"); @@ -1063,7 +1070,7 @@ dav_put(const char *path, const char *cache_path, int *exists, time_t *expire, int -dav_quota(const char *path, off_t *available, off_t *used) +dav_quota(const char *path, off64_t *total, off64_t *used) { int ret; if (!initialized) { @@ -1071,26 +1078,52 @@ dav_quota(const char *path, off_t *available, off_t *used) if (ret) return ret; } + static int use_rfc = 1; + static int use_userinfo = 1; + quota_context ctx; - ctx.available = (off_t) -1; - ctx.used = (off_t) -1; - + ctx.error = 0; + ctx.total = 0; + ctx.used = 0; + ret = EIO; char *spath = ne_path_escape(path); - ne_propfind_handler *ph = ne_propfind_create(session, spath, NE_DEPTH_ZERO); - ret = ne_propfind_named(ph, quota_names, quota_result, &ctx); - ret = get_error(ret, "PROPFIND"); - ne_propfind_destroy(ph); - free(spath); - if (!ret) { - if (ctx.available == (off_t) -1 || ctx.used == (off_t) -1) { + if (use_rfc) { + ne_propfind_handler *ph = ne_propfind_create(session, spath, + NE_DEPTH_ZERO); + ret = ne_propfind_named(ph, quota_names, quota_result, &ctx); + ret = get_error(ret, "PROPFIND"); + ne_propfind_destroy(ph); + + if (!ret && ctx.error) { ret = EIO; - } else { - *available = ctx.available; - *used = ctx.used; + if (ctx.error == 2) + use_rfc = 0; } } + if (ret && use_userinfo) { + ctx.error = 0; + ne_request *req = ne_request_create(session, "USERINFO", spath); + ne_add_response_body_reader(req, ne_accept_2xx, quota_reader, &ctx); + ret = ne_request_dispatch(req); + ret = get_error(ret, "USERINFO"); + ne_request_destroy(req); + + if (!ret) { + if (ctx.error) + ret = EIO; + } else if (ret == EINVAL) { + use_userinfo = 0; + } + } + + if (!ret) { + *total = ctx.total; + *used = ctx.used; + } + + free(spath); return ret; } @@ -1519,7 +1552,7 @@ auth(void *userdata, const char *realm, int attempt, char *user, char *pwd) } -/* Writes data from block to a local file. +/* Reads HTTP-data from blockand writes them to a local file. userdata must be a get_context structure that holds at least the name of the local file. If it does not contain a file descriptor, the file is opened for writing and the file descriptor is stored in the get_context @@ -1530,7 +1563,7 @@ auth(void *userdata, const char *realm, int attempt, char *user, char *pwd) length : Number of bytes in the buffer. return value : 0 on success, EIO otherwise. */ static int -block_writer(void *userdata, const char *block, size_t length) +file_reader(void *userdata, const char *block, size_t length) { get_context *ctx = (get_context *) userdata; if (!ctx->fd) @@ -1733,6 +1766,36 @@ prop_result(void *userdata, const ne_uri *uri, const ne_prop_result_set *set) } +static int +quota_reader(void *userdata, const char *block, size_t length) +{ + if (length < 1) return 0; + quota_context *ctx = (quota_context *) userdata; + + char *quota = strndup(block, length); + if (!quota) { + ctx->error = 1; + return 0; + } + + char *number = strtok(quota, ","); + if (number) { + ctx->total = strtoull(number, NULL, 10); + } else { + ctx->error = 1; + free(quota); + return 0; + } + + number = strtok(NULL, ","); + if (number) + ctx->used = strtoull(number, NULL, 10); + + free(quota); + return 0; +} + + /* Reads available and used bytes from set and stores them in userdata. */ #if NE_VERSION_MINOR < 26 @@ -1756,12 +1819,23 @@ quota_result(void *userdata, const ne_uri *uri, const ne_prop_result_set *set) #endif /* NE_VERSION_MINOR >= 26 */ const char *data = ne_propset_value(set, "a_names[AVAILABLE]); - if (data) - ctx->available = strtol(data, NULL, 10); + if (data) { + ctx->total = strtoull(data, NULL, 10); + } else { + const ne_status *st = ne_propset_status(set, "a_names[AVAILABLE]); + if (st && st->klass == 4) { + ctx->error = 2; + } else { + ctx->error = 1; + } + return; + } data = ne_propset_value(set, "a_names[USED]); if (data) - ctx->used = strtol(data, NULL, 10); + ctx->used = strtoull(data, NULL, 10); + + ctx->total += ctx->used; } diff --git a/src/webdav.h b/src/webdav.h index 89dcbb1..69e3814 100644 --- a/src/webdav.h +++ b/src/webdav.h @@ -247,10 +247,12 @@ dav_put(const char *path, const char *cache_path, int *exists, time_t *expire, char **etag, time_t *mtime, char **mime, int execute); /* Makes a PROPFIND request for path to get quota information (RFC 4331) - and places them in available and used. If quota information is not + and places them in total and used. + toatal is the sum of quota-available-bytes and quota-used-bytes. + If quota information is not available, an error is returned and available and used are not changed. */ int -dav_quota(const char *path, off_t *available, off_t *used); +dav_quota(const char *path, off_t *total, off_t *used); /* Sets or resets the execute property of file path.