From: Frank Lahm Date: Wed, 23 Nov 2011 13:58:09 +0000 (+0100) Subject: Rewritten TM used size calculation X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a951bdd9b6f82783cc92024da4c2761292db8a5b;hp=76f5b97838bea8d7252278cf9cc4e342e7a6ff2a;p=netatalk.git Rewritten TM used size calculation --- diff --git a/etc/afpd/unix.c b/etc/afpd/unix.c index 53a59b35..9eafa7e0 100644 --- a/etc/afpd/unix.c +++ b/etc/afpd/unix.c @@ -52,7 +52,7 @@ char *strchr (), *strrchr (); */ int ustatfs_getvolspace(const struct vol *vol, VolSpace *bfree, VolSpace *btotal, u_int32_t *bsize) { - VolSpace maxVolSpace = (~(VolSpace)0); + VolSpace maxVolSpace = INTMAX_MAX; #ifdef ultrix struct fs_data sfs; diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c index 9b9672ab..75049ec6 100644 --- a/etc/afpd/volume.c +++ b/etc/afpd/volume.c @@ -52,6 +52,7 @@ char *strchr (), *strrchr (); #include #include #include +#include #ifdef CNID_DB #include @@ -1444,64 +1445,148 @@ static void volume_unlink(struct vol *volume) } } } - -static off_t getused_size; /* result of getused() */ - /*! - nftw callback for getused() + * Read band-size info from Info.plist XML file of an TM sparsebundle + * + * @param path (r) path to Info.plist file + * @return band-size in bytes, -1 on error */ -static int getused_stat(const char *path, - const struct stat *statp, - int tflag, - struct FTW *ftw) +static long long int get_tm_bandsize(const char *path) { - off_t low, high; + EC_INIT; + FILE *file = NULL; + char buf[512]; + long long int bandsize = -1; + + LOG(log_error, logtype_afpd, "get_tm_bandsize(\"%s\")", path); + + EC_NULL_LOGSTR(file = fopen(path, "r"), + "get_tm_bandsize(\"%s\"): %s", + path, strerror(errno)); - if (tflag == FTW_F || tflag == FTW_D) { - getused_size += statp->st_blocks * 512; + while (fgets(buf, sizeof(buf), file) != NULL) { + if (strstr(buf, "band-size") == NULL) + continue; + + if (fscanf(file, " %lld", &bandsize) != 1) { + LOG(log_error, logtype_afpd, "get_tm_bandsize(\"%s\"): can't parse band-size", path); + EC_FAIL; + } + break; } - return 0; +EC_CLEANUP: + if (file) + fclose(file); + return bandsize; } -#define GETUSED_CACHETIME 5 /*! - * Calculate used size of a volume with nftw + * Return number on entries in a directory * - * The result is cached, we're try to avoid frequently calling nftw() + * @param path (r) path to dir + * @return number of entries, -1 on error + */ +static long long int get_tm_bands(const char *path) +{ + EC_INIT; + long long int count = 0; + DIR *dir = NULL; + const struct dirent *entry; + + EC_NULL(dir = opendir(path)); + + while ((entry = readdir(dir)) != NULL) + count++; + count -= 2; /* All OSens I'm aware of return "." and "..", so just substract them, avoiding string comparison in loop */ + +EC_CLEANUP: + if (dir) + closedir(dir); + if (ret != 0) + return -1; + return count; +} + +/*! + * Calculate used size of a TimeMachine volume + * + * This assumes that the volume is used only for TimeMachine. * - * 1) Call nftw an refresh if: - * 1a) - we're called the first time - * 1b) - volume modification date is not yet set and the last time we've been called is - * longer then 30 sec ago - * 1c) - the last volume modification is less then 30 sec old + * 1) readdir(path of volume) + * 2) for every element that matches regex "\(.*\)\.sparsebundle$" : + * 3) parse "\1.sparsebundle/Info.plist" and read the band-size XML key integer value + * 4) stat "\1.sparsebundle/bands/" + * 5) calculate used size as: (st_nlink - 1) * band-size * * @param vol (rw) volume to calculate + * @return Estimated used size in bytes, -1 on error */ -static int getused(struct vol *vol) +#define TM_USED_CACHETIME 60 /* cache for 60 seconds */ +static VolSpace get_tm_used(struct vol *vol) { - static time_t vol_mtime = 0; - int ret = 0; + EC_INIT; + long long int bandsize; + VolSpace used = 0; + bstring infoplist = NULL; + bstring bandsdir = NULL; + DIR *dir = NULL; + const struct dirent *entry; + const char *p; + struct stat st; + long int links; time_t now = time(NULL); - if (!vol_mtime - || (!vol->v_mtime && ((vol_mtime + GETUSED_CACHETIME) < now)) - || (vol->v_mtime && ((vol_mtime + GETUSED_CACHETIME) < vol->v_mtime)) - ) { - vol_mtime = now; - getused_size = 0; - vol->v_written = 0; - ret = nftw(vol->v_path, getused_stat, NULL, 20, FTW_PHYS); /* 2 */ - LOG(log_debug, logtype_afpd, "volparams: from nftw: %" PRIu64 " bytes", - getused_size); - } else { - getused_size += vol->v_written; + if (vol->v_tm_cachetime + && ((vol->v_tm_cachetime + TM_USED_CACHETIME) >= now)) { + if (vol->v_tm_used == -1) + return -1; + vol->v_tm_used += vol->v_written; vol->v_written = 0; - LOG(log_debug, logtype_afpd, "volparams: cached used: %" PRIu64 " bytes", - getused_size); + LOG(log_error, logtype_afpd, "getused(%s): used(cached): %jd", vol->v_path, (intmax_t)vol->v_tm_used); + return vol->v_tm_used; } - return ret; + vol->v_tm_cachetime = now; + + EC_NULL(dir = opendir(vol->v_path)); + + while ((entry = readdir(dir)) != NULL) { + if (((p = strstr(entry->d_name, "sparsebundle")) != NULL) + && (strlen(entry->d_name) == (p + strlen("sparsebundle") - entry->d_name))) { + LOG(log_error, logtype_afpd, "getused(\"%s\"): %s", vol->v_path, entry->d_name); + + EC_NULL_LOG(infoplist = bformat("%s/%s/%s", vol->v_path, entry->d_name, "Info.plist")); + + if ((bandsize = get_tm_bandsize(cfrombstr(infoplist))) == -1) + continue; + + LOG(log_error, logtype_afpd, "getused: %s, bandsize: %lld", cfrombstr(infoplist), bandsize); + + EC_NULL_LOG(bandsdir = bformat("%s/%s/%s/", vol->v_path, entry->d_name, "bands")); + + if ((links = get_tm_bands(cfrombstr(bandsdir))) == -1) + continue; + + LOG(log_error, logtype_afpd, "getused: %s, links: %ld", cfrombstr(bandsdir), links); + + used += (links - 1) * bandsize; + LOG(log_error, logtype_afpd, "getused: %s, used: %jd", cfrombstr(bandsdir), (intmax_t)used); + } + } + +EC_CLEANUP: + if (ret != 0) + used = -1; + vol->v_tm_used = used; + if (infoplist) + bdestroy(infoplist); + if (bandsdir) + bdestroy(bandsdir); + if (dir) + closedir(dir); + LOG(log_error, logtype_afpd, "getused(%s), used: %jd", vol->v_path, (intmax_t)used); + return used; } static int getvolspace(struct vol *vol, @@ -1533,6 +1618,8 @@ static int getvolspace(struct vol *vol, if (( rc = ustatfs_getvolspace( vol, xbfree, xbtotal, bsize)) != AFP_OK ) { return( rc ); } + LOG(log_error, logtype_afpd, "volparams: total: %jd, free: %jd", + (intmax_t)(*xbtotal), (intmax_t)(*xbfree)); #define min(a,b) ((a)<(b)?(a):(b)) #ifndef NO_QUOTA_SUPPORT @@ -1549,14 +1636,16 @@ static int getvolspace(struct vol *vol, getvolspace_done: if (vol->v_limitsize) { - if (getused(vol) != 0) + if ((used = get_tm_used(vol)) == -1) return AFPERR_MISC; - LOG(log_debug, logtype_afpd, "volparams: used on volume: %" PRIu64 " bytes", - getused_size); - vol->v_tm_used = getused_size; + LOG(log_error, logtype_afpd, "volparams: total: %jd, used: %jd, free: %jd", + (intmax_t)(*xbtotal), (intmax_t)used, (intmax_t)(*xbfree)); *xbtotal = min(*xbtotal, (vol->v_limitsize * 1024 * 1024)); - *xbfree = min(*xbfree, *xbtotal < getused_size ? 0 : *xbtotal - getused_size); + *xbfree = min(*xbfree, *xbtotal < used ? 0 : *xbtotal - used); + + LOG(log_error, logtype_afpd, "volparams: total: %jd, used: %jd, free: %jd", + (intmax_t)(*xbtotal), (intmax_t)used, (intmax_t)(*xbfree)); } *bfree = min( *xbfree, maxsize); diff --git a/include/atalk/volume.h b/include/atalk/volume.h index 01de038d..84a57c1d 100644 --- a/include/atalk/volume.h +++ b/include/atalk/volume.h @@ -20,7 +20,7 @@ #define AFPVOL_U8MNAMELEN 255 /* AFP3 sepc */ #define AFPVOL_MACNAMELEN 27 /* AFP2 spec */ -typedef u_int64_t VolSpace; +typedef off_t VolSpace; struct vol { struct vol *v_next; @@ -65,8 +65,9 @@ struct vol { char *v_gvs; void *v_nfsclient; int v_nfs; - uintmax_t v_tm_used; /* used bytes on a TM volume */ - uintmax_t v_written; /* amount of data written in afp_write, reset every time a FCE_TM_SIZE event is sent */ + off_t v_tm_used; /* used bytes on a TM volume */ + time_t v_tm_cachetime; /* time at which v_tm_used was calculated last */ + off_t v_written; /* amount of data written in afp_write, reset every time a FCE_TM_SIZE event is sent */ /* only when opening/closing volumes or in error */ int v_casefold;