From c27da5ec4487778eb8c60cd52d480a97b099cbdc Mon Sep 17 00:00:00 2001 From: Frank Lahm Date: Mon, 1 Nov 2010 17:31:43 +0100 Subject: [PATCH] Enhance dircache robustness, dircache statistics logged in SIGALARM --- etc/afpd/afp_dsi.c | 2 + etc/afpd/catsearch.c | 2 +- etc/afpd/dircache.c | 149 +++++++++++++++++++++++++++++++------- etc/afpd/dircache.h | 8 +- etc/afpd/directory.c | 26 ++++--- etc/afpd/directory.h | 2 +- etc/afpd/enumerate.c | 10 +-- etc/afpd/file.c | 22 ++++-- etc/afpd/filedir.c | 10 +-- etc/afpd/volume.c | 4 +- include/atalk/directory.h | 3 +- 11 files changed, 172 insertions(+), 66 deletions(-) diff --git a/etc/afpd/afp_dsi.c b/etc/afpd/afp_dsi.c index 90facbee..277e412b 100644 --- a/etc/afpd/afp_dsi.c +++ b/etc/afpd/afp_dsi.c @@ -214,6 +214,8 @@ static void alarm_handler(int sig _U_) * may use alarm() */ setitimer(ITIMER_REAL, &dsi->timer, NULL); + log_dircache_stat(); + /* we got some traffic from the client since the previous timer * tick. */ if ((child.flags & CHILD_DATA)) { diff --git a/etc/afpd/catsearch.c b/etc/afpd/catsearch.c index 3aa04349..70c93f15 100644 --- a/etc/afpd/catsearch.c +++ b/etc/afpd/catsearch.c @@ -600,7 +600,7 @@ static int catsearch(struct vol *vol, struct dir *dir, ALL dirsearch_byname will fail. */ int unlen = strlen(path.u_name); - path.d_dir = dircache_search_by_name(vol, dstack[cidx].dir, path.u_name, unlen); + path.d_dir = dircache_search_by_name(vol, dstack[cidx].dir, path.u_name, unlen, path.st.st_ctime); if (path.d_dir == NULL) { /* path.m_name is set by adddir */ if (NULL == (path.d_dir = dir_add( vol, dstack[cidx].dir, &path, unlen) ) ) { diff --git a/etc/afpd/dircache.c b/etc/afpd/dircache.c index a01139a2..41635beb 100644 --- a/etc/afpd/dircache.c +++ b/etc/afpd/dircache.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -70,7 +71,19 @@ * The dircache is a LRU cache, whenever it fills up we call dircache_evict internally which removes * DIRCACHE_FREE_QUANTUM elements from the cache. * - * There is only one cache for all volumes, so of course we use the volume is in hashing calculations. + * There is only one cache for all volumes, so of course we use the volume id in hashing calculations. + * + * In order to avoid cache poisoning, we store the cached entries st_ctime from stat in + * struct dir.ctime_dircache. Later when we search the cache we compare the stored + * value with the result of a fresh stat. If the times differ, we remove the cached + * entry and return "no entry found in cache". + * A elements ctime changes when + * 1) the element is renamed + * (we loose the cached entry here, but it will expire when the cache fills) + * 2) its a directory and an object has been created therein + * 3) the element is deleted and recreated under the same name + * Using ctime leads to cache eviction in case 2) where it wouldn't be necessary, because + * the dir itself (name, CNID, ...) hasn't changed, but there's no other way. * * Indexes * ======= @@ -102,6 +115,16 @@ static hash_t *dircache; /* The actual cache */ static unsigned int dircache_maxsize; /* cache maximum size */ +static struct dircache_stat { + unsigned long long lookups; + unsigned long long hits; + unsigned long long misses; + unsigned long long added; + unsigned long long removed; + unsigned long long expunged; + unsigned long long evicted; +} dircache_stat; + /* FNV 1a */ static hash_val_t hash_vid_did(const void *key) { @@ -210,7 +233,7 @@ static int hash_comp_didname(const void *k1, const void *k2) * queue index on dircache */ static q_t *index_queue; /* the index itself */ -static unsigned int queue_count; +static unsigned long queue_count; /*! * @brief Remove a fixed number of (oldest) entries from the cache and indexes @@ -251,8 +274,8 @@ static void dircache_evict(void) dir_free(dir); /* 4 */ } - AFP_ASSERT(queue_count == dircache->hash_nodecount); - + AFP_ASSERT(queue_count == dircache->hash_nodecount); + dircache_stat.evicted += DIRCACHE_FREE_QUANTUM; LOG(log_debug, logtype_afpd, "dircache: {finished cache eviction}"); } @@ -262,50 +285,92 @@ static void dircache_evict(void) ********************************************************/ /*! - * @brief Search the dircache via a CNID + * @brief Search the dircache via a CNID for a directory + * + * Found cache entries are expunged if both the parent directory st_ctime and the objects + * st_ctime are modified. + * This func builds on the fact, that all our code only ever needs to and does search + * the dircache by CNID expecting directories to be returned, but not files. + * Thus + * (1) if we find a file (d_fullpath == NULL) for a given CNID we + * (1a) remove it from the cache + * (1b) return NULL indicating nothing found + * (2) we can then use d_fullpath to stat the directory * - * @param vol (r) pointer to struct vol - * @param cnid (r) CNID of the file or directory + * @param vol (r) pointer to struct vol + * @param cnid (r) CNID of the directory to search * - * @returns Pointer to struct dir if found, else NULL + * @returns Pointer to struct dir if found, else NULL */ struct dir *dircache_search_by_did(const struct vol *vol, cnid_t cnid) { struct dir *cdir = NULL; struct dir key; + struct stat st; hnode_t *hn; - AFP_ASSERT(vol); - AFP_ASSERT(ntohl(cnid) >= CNID_START); + AFP_ASSERT(vol); + AFP_ASSERT(ntohl(cnid) >= CNID_START); + dircache_stat.lookups++; key.d_vid = vol->v_vid; key.d_did = cnid; if ((hn = hash_lookup(dircache, &key))) cdir = hnode_get(hn); - if (cdir) - LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {cached: path:'%s'}", - ntohl(cnid), cfrombstr(cdir->d_u_name)); - else - LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {not in cache}", ntohl(cnid)); + if (cdir) { + if (cdir->d_fullpath == NULL) { /* (1) */ + LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {not a directory:\"%s\"}", + ntohl(cnid), cfrombstr(cdir->d_u_name)); + (void)dir_remove(vol, cdir); /* (1a) */ + dircache_stat.expunged++; + return NULL; /* (1b) */ + } + if (lstat(cfrombstr(cdir->d_fullpath), &st) != 0) { + LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {missing:\"%s\"}", + ntohl(cnid), cfrombstr(cdir->d_fullpath)); + (void)dir_remove(vol, cdir); + dircache_stat.expunged++; + return NULL; + } + if (cdir->ctime_dircache != st.st_ctime) { + LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {modified:\"%s\"}", + ntohl(cnid), cfrombstr(cdir->d_u_name)); + (void)dir_remove(vol, cdir); + dircache_stat.expunged++; + return NULL; + } + LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {cached: path:\"%s\"}", + ntohl(cnid), cfrombstr(cdir->d_fullpath)); + dircache_stat.hits++; + } else { + LOG(log_debug, logtype_afpd, "dircache(cnid:%u): {not in cache}", ntohl(cnid)); + dircache_stat.hits++; + } + return cdir; } /*! * @brief Search the cache via did/name hashtable * - * @param vol (r) volume - * @param dir (r) directory - * @param name (r) name (server side encoding) - * @parma len (r) strlen of name + * Found cache entries are expunged if both the parent directory st_ctime and the objects + * st_ctime are modified. + * + * @param vol (r) volume + * @param dir (r) directory + * @param name (r) name (server side encoding) + * @parma len (r) strlen of name + * @param ctime (r) current st_ctime from stat * * @returns pointer to struct dir if found in cache, else NULL */ -struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir, char *name, int len) +struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir, char *name, int len, time_t ctime) { struct dir *cdir = NULL; struct dir key; + hnode_t *hn; static_bstring uname = {-1, len, (unsigned char *)name}; @@ -315,6 +380,7 @@ struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir AFP_ASSERT(len == strlen(name)); AFP_ASSERT(len < 256); + dircache_stat.lookups++; LOG(log_debug, logtype_afpd, "dircache_search_by_name(did:%u, \"%s\")", ntohl(dir->d_did), name); @@ -327,12 +393,22 @@ struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir cdir = hnode_get(hn); } - if (cdir) - LOG(log_debug, logtype_afpd, "dircache(did:%u, '%s'): {found in cache}", + if (cdir) { + if (cdir->ctime_dircache != ctime) { + LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {modified}", + ntohl(dir->d_did), name); + (void)dir_remove(vol, cdir); + dircache_stat.expunged++; + return NULL; + } + LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {found in cache}", ntohl(dir->d_did), name); - else - LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {not in cache}", + dircache_stat.hits++; + } else { + LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {not in cache}", ntohl(dir->d_did), name); + dircache_stat.misses++; + } return cdir; } @@ -379,6 +455,7 @@ int dircache_add(struct dir *dir) queue_count++; } + dircache_stat.added++; LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {added}", ntohl(dir->d_did), cfrombstr(dir->d_u_name)); @@ -433,8 +510,9 @@ void dircache_remove(const struct vol *vol _U_, struct dir *dir, int flags) LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {removed}", ntohl(dir->d_did), cfrombstr(dir->d_u_name)); - AFP_ASSERT(queue_count == index_didname->hash_nodecount - && queue_count == dircache->hash_nodecount); + dircache_stat.removed++; + AFP_ASSERT(queue_count == index_didname->hash_nodecount + && queue_count == dircache->hash_nodecount); } /*! @@ -484,6 +562,23 @@ int dircache_init(int reqsize) return 0; } +/*! + * Log dircache statistics + */ +void log_dircache_stat(void) +{ + LOG(log_debug, logtype_afpd, "dircache_stat: " + "entries: %lu, lookups: %llu, hits: %llu, misses: %llu, added: %llu, removed: %llu, expunged: %llu, evicted: %llu", + queue_count, + dircache_stat.lookups, + dircache_stat.hits, + dircache_stat.misses, + dircache_stat.added, + dircache_stat.removed, + dircache_stat.expunged, + dircache_stat.evicted); +} + /*! * @brief Dump dircache to /tmp/dircache.PID */ @@ -506,7 +601,7 @@ void dircache_dump(void) } setbuf(dump, NULL); - fprintf(dump, "Number of cache entries in LRU queue: %u\n", queue_count); + fprintf(dump, "Number of cache entries in LRU queue: %lu\n", queue_count); fprintf(dump, "Configured maximum cache size: %u\n\n", dircache_maxsize); fprintf(dump, "Primary CNID index:\n"); diff --git a/etc/afpd/dircache.h b/etc/afpd/dircache.h index d261ea57..69c5f9ab 100644 --- a/etc/afpd/dircache.h +++ b/etc/afpd/dircache.h @@ -1,5 +1,4 @@ /* - $Id: dircache.h,v 1.1.2.5 2010-02-11 14:13:06 franklahm Exp $ Copyright (c) 2010 Frank Lahm This program is free software; you can redistribute it and/or modify @@ -16,6 +15,8 @@ #ifndef DIRCACHE_H #define DIRCACHE_H +#include + #include #include @@ -28,12 +29,13 @@ #define DIRCACHE (1 << 0) #define DIDNAME_INDEX (1 << 1) #define QUEUE_INDEX (1 << 2) +#define DIRCACHE_ALL (DIRCACHE|DIDNAME_INDEX|QUEUE_INDEX) extern int dircache_init(int reqsize); extern int dircache_add(struct dir *); extern void dircache_remove(const struct vol *, struct dir *, int flag); extern struct dir *dircache_search_by_did(const struct vol *vol, cnid_t did); -extern struct dir *dircache_search_by_name(const struct vol *, const struct dir *dir, char *name, int len); +extern struct dir *dircache_search_by_name(const struct vol *, const struct dir *dir, char *name, int len, time_t ctime); extern void dircache_dump(void); - +extern void log_dircache_stat(void); #endif /* DIRCACHE_H */ diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index e91ee70a..61a385f1 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -555,12 +555,12 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did) } /* Create struct dir */ - if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath)) == NULL) { /* 6 */ + if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */ LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno)); err = 1; goto exit; } - + /* Add it to the cache only if it's a dir */ if (dircache_add(ret) != 0) { /* 7 */ err = 1; @@ -677,7 +677,8 @@ int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) * @param vol (r) pointer to struct vol * @param pdid (r) Parent CNID * @param did (r) CNID - * @param fullpath (r) Full unix path to dir or NULL for files + * @param path (r) Full unix path to dir or NULL for files + * @param ctime (r) st_ctime from stat * * @returns pointer to new struct dir or NULL on error * @@ -688,7 +689,8 @@ struct dir *dir_new(const char *m_name, const struct vol *vol, cnid_t pdid, cnid_t did, - bstring path) + bstring path, + time_t ctime) { struct dir *dir; @@ -722,6 +724,7 @@ struct dir *dir_new(const char *m_name, dir->d_pdid = pdid; dir->d_vid = vol->v_vid; dir->d_fullpath = path; + dir->ctime_dircache = ctime; return dir; } @@ -746,8 +749,7 @@ void dir_free(struct dir *dir) * @brief Create struct dir from struct path * * Create a new struct dir from struct path. Then add it to the cache. - * The caller must have assured that the dir is not already in the cache, - * cf theAFP_ASSERTion. + * * 1. Open adouble file, get CNID from it. * 2. Search the database, hinting with the CNID from (1). * 3. Build fullpath and create struct dir. @@ -776,7 +778,7 @@ struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, i AFP_ASSERT(path); AFP_ASSERT(len > 0); - if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name))) != NULL) { + if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name), path->st.st_ctime)) != NULL) { /* there's a stray entry in the dircache */ LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}", ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name, @@ -819,7 +821,7 @@ struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, i } /* Allocate and initialize struct dir */ - if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath)) == NULL) { /* 3 */ + if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath, path->st.st_ctime)) == NULL) { /* 3 */ err = 4; goto exit; } @@ -1169,7 +1171,7 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) /* Search the cache */ int unamelen = strlen(ret.u_name); - cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen); /* 14 */ + cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen, ret.st.st_ctime); /* 14 */ if (cdir == NULL) { /* Not in cache, create one */ if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */ @@ -2332,7 +2334,7 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r return AFPERR_PARAM; LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request"); uuidtype_t type; - len = getnamefromuuid( ibuf, &name, &type); + len = getnamefromuuid((unsigned char*) ibuf, &name, &type); if (len != 0) /* its a error code, not len */ return AFPERR_NOITEM; switch (type) { @@ -2464,13 +2466,13 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz break; case 5 : /* username -> UUID */ LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf); - if (0 != getuuidfromname(ibuf, UUID_USER, rbuf)) + if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf)) return AFPERR_NOITEM; *rbuflen = UUID_BINSIZE; break; case 6 : /* groupname -> UUID */ LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf); - if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf)) + if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf)) return AFPERR_NOITEM; *rbuflen = UUID_BINSIZE; break; diff --git a/etc/afpd/directory.h b/etc/afpd/directory.h index c9e193d9..4b9ed48f 100644 --- a/etc/afpd/directory.h +++ b/etc/afpd/directory.h @@ -109,7 +109,7 @@ struct maccess { typedef int (*dir_loop)(struct dirent *, char *, void *); extern struct dir *dir_new(const char *mname, const char *uname, const struct vol *, - cnid_t pdid, cnid_t did, bstring fullpath); /* volume.c needs it once */ + cnid_t pdid, cnid_t did, bstring fullpath, time_t ctime); extern void dir_free (struct dir *); extern struct dir *dir_add(struct vol *, const struct dir *, struct path *, int); extern int dir_modify(const struct vol *vol, struct dir *dir, cnid_t pdid, cnid_t did, diff --git a/etc/afpd/enumerate.c b/etc/afpd/enumerate.c index 49625bdc..79869dbd 100644 --- a/etc/afpd/enumerate.c +++ b/etc/afpd/enumerate.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -268,10 +269,8 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, return path_error(o_path, AFPERR_NODIR ); } - LOG(log_debug, logtype_afpd, "enumerate(vid:%u, did:%u, cwddid:%u, cwd:'%s', name:'%s', f/d:%04x/%04x, rc:%u, i:%u, max:%u)", - ntohs(vid), ntohl(did), ntohl(curdir->d_did), - cfrombstr(curdir->d_fullpath), o_path->u_name, - fbitmap, dbitmap, reqcnt, sindex, maxsz); + LOG(log_debug, logtype_afpd, "enumerate(\"%s/%s\", f/d:%04x/%04x, rc:%u, i:%u, max:%u)", + getcwdpath(), o_path->u_name, fbitmap, dbitmap, reqcnt, sindex, maxsz); data = rbuf + 3 * sizeof( u_int16_t ); sz = 3 * sizeof( u_int16_t ); /* fbitmap, dbitmap, reqcount */ @@ -375,7 +374,7 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, continue; } int len = strlen(s_path.u_name); - if ((dir = dircache_search_by_name(vol, curdir, s_path.u_name, len)) == NULL) { + if ((dir = dircache_search_by_name(vol, curdir, s_path.u_name, len, s_path.st.st_ctime)) == NULL) { if ((dir = dir_add(vol, curdir, &s_path, len)) == NULL) { LOG(log_error, logtype_afpd, "enumerate(vid:%u, did:%u, name:'%s'): error adding dir: '%s'", ntohs(vid), ntohl(did), o_path->u_name, s_path.u_name); @@ -389,6 +388,7 @@ static int enumerate(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, if ( fbitmap == 0 ) { continue; } + /* files are added to the dircache in getfilparams() -> getmetadata() */ if (AFP_OK != ( ret = getfilparams(vol, fbitmap, &s_path, curdir, data + header , &esz )) ) { return( ret ); diff --git a/etc/afpd/file.c b/etc/afpd/file.c index 1528003b..d3bf5600 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -326,7 +326,7 @@ int getmetadata(struct vol *vol, if (!path->id) { struct dir *cachedfile; int len = strlen(upath); - if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL) + if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL) id = cachedfile->d_did; else { id = get_id(vol, adp, st, dir->d_did, upath, len); @@ -343,10 +343,11 @@ int getmetadata(struct vol *vol, } } - if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL)) == NULL) { + if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) { LOG(log_error, logtype_afpd, "getmetadata: error from dir_new"); exit(EXITERR_SYS); } + if ((dircache_add(cachedfile)) != 0) { LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error"); exit(EXITERR_SYS); @@ -2235,12 +2236,11 @@ int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U /* id's need switching. src -> dest and dest -> src. * we need to re-stat() if it was a cross device copy. */ - if (sid) { - cnid_delete(vol->v_cdb, sid); - } - if (did) { - cnid_delete(vol->v_cdb, did); - } + if (sid) + cnid_delete(vol->v_cdb, sid); + if (did) + cnid_delete(vol->v_cdb, did); + if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0)) || @@ -2334,5 +2334,11 @@ err_exchangefile: ad_close(addp, ADFLAGS_HF); } + struct dir *cached; + if ((cached = dircache_search_by_did(vol, sid)) != NULL) + (void)dir_remove(vol, cached); + if ((cached = dircache_search_by_did(vol, did)) != NULL) + (void)dir_remove(vol, cached); + return err; } diff --git a/etc/afpd/filedir.c b/etc/afpd/filedir.c index 16a32ef6..be7056a7 100644 --- a/etc/afpd/filedir.c +++ b/etc/afpd/filedir.c @@ -463,12 +463,10 @@ static int moveandrename(const struct vol *vol, /* Remove it from the cache */ struct dir *cacheddir = dircache_search_by_did(vol, id); - if (cacheddir == NULL) { - LOG(log_warning, logtype_afpd,"Not cached: \"%s/%s\"", getcwdpath(), upath); - rc = AFPERR_MISC; - goto exit; + if (cacheddir) { + LOG(log_warning, logtype_afpd,"Still cached: \"%s/%s\"", getcwdpath(), upath); + (void)dir_remove(vol, cacheddir); } - (void)dir_remove(vol, cacheddir); /* fix up the catalog entry */ cnid_update(vol->v_cdb, id, st, curdir->d_did, upath, strlen(upath)); @@ -608,7 +606,7 @@ int afp_delete(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size rc = deletefile(vol, -1, upath, 1); struct dir *cachedfile; - if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath)))) { + if ((cachedfile = dircache_search_by_name(vol, dir, upath, strlen(upath), s_path->st.st_ctime))) { dircache_remove(vol, cachedfile, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); dir_free(cachedfile); } diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c index 257ca3ec..e4dc0410 100644 --- a/etc/afpd/volume.c +++ b/etc/afpd/volume.c @@ -2172,7 +2172,8 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t volume, DIRDID_ROOT_PARENT, DIRDID_ROOT, - bfromcstr(volume->v_path)) + bfromcstr(volume->v_path), + st.st_ctime) ) == NULL) { free(vol_mname); LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) ); @@ -2180,7 +2181,6 @@ int afp_openvol(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t goto openvol_err; } free(vol_mname); - volume->v_root = dir; curdir = dir; diff --git a/include/atalk/directory.h b/include/atalk/directory.h index dd889dbb..079d01c3 100644 --- a/include/atalk/directory.h +++ b/include/atalk/directory.h @@ -58,7 +58,8 @@ struct dir { ucs2_t *d_m_name_ucs2; /* mac name as UCS2 */ qnode_t *qidx_node; /* pointer to position in queue index */ void *d_ofork; /* oforks using this directory. */ - time_t ctime; /* inode ctime */ + time_t ctime; /* inode ctime, used and modified by reenumeration */ + time_t ctime_dircache; /* inode ctime, used and modified by dircache */ int d_flags; /* directory flags */ cnid_t d_pdid; /* CNID of parent directory */ cnid_t d_did; /* CNID of directory */ -- 2.39.2