* a struct dir is initialized, the fullpath to the directory is stored there.
*
* In order to speed up the CNID query for files too, which eg happens when a directory is enumerated,
- * files are stored too in the dircache. In order to differentiate between files and dirs, we re-use
- * the element fullpath, which for files is always NULL.
+ * files are stored too in the dircache. In order to differentiate between files and dirs, we set
+ * the flag DIRF_ISFILE in struct dir.d_flags for files.
*
* The most frequent codepatch that leads to caching is directory enumeration (cf enumerate.c):
* - if a element is a directory:
* 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
+ * (1) if we find a file 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
cdir = hnode_get(hn);
if (cdir) {
- if (cdir->d_fullpath == NULL) { /* (1) */
+ if (cdir->d_flags & DIRF_ISFILE) { /* (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;
}
- if (cdir->ctime_dircache != st.st_ctime) {
+ if (cdir->dcache_ctime != 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);
return NULL;
}
- if (cdir->ctime_dircache != st.st_ctime) {
+ /* Remove modified directories (precaution, probably not necessary) and file */
+ if (
+ (!(cdir->d_flags & DIRF_ISFILE) && (cdir->dcache_ctime != st.st_ctime))
+ ||
+ ((cdir->d_flags & DIRF_ISFILE) && (cdir->dcache_ino != st.st_ino))
+ ) {
LOG(log_debug, logtype_afpd, "dircache(did:%u,\"%s\"): {modified}",
ntohl(dir->d_did), name);
(void)dir_remove(vol, cdir);
ntohs(dir->d_vid),
ntohl(dir->d_pdid),
ntohl(dir->d_did),
- dir->d_fullpath ? "d" : "f",
- cfrombstr(dir->d_u_name));
+ dir->d_flags & DIRF_ISFILE ? "f" : "d",
+ cfrombstr(dir->d_fullpath));
}
fprintf(dump, "\nSecondary DID/name index:\n");
ntohs(dir->d_vid),
ntohl(dir->d_pdid),
ntohl(dir->d_did),
- dir->d_fullpath ? "d" : "f",
- cfrombstr(dir->d_u_name));
+ dir->d_flags & DIRF_ISFILE ? "f" : "d",
+ cfrombstr(dir->d_fullpath));
}
fprintf(dump, "\nLRU Queue:\n");
ntohs(dir->d_vid),
ntohl(dir->d_pdid),
ntohl(dir->d_did),
- dir->d_fullpath ? "d" : "f",
- cfrombstr(dir->d_u_name));
+ dir->d_flags & DIRF_ISFILE ? "f" : "d",
+ cfrombstr(dir->d_fullpath));
n = n->next;
}
* Resolve a DID, allocate a struct dir for it
* 1. Check for special CNIDs 0 (invalid), 1 and 2.
* 2a. Check if the DID is in the cache.
- * 2b. Check if it's really a dir (d_fullpath != NULL) because we cache files too.
+ * 2b. Check if it's really a dir because we cache files too.
* 3. If it's not in the cache resolve it via the database.
* 4. Build complete server-side path to the dir.
* 5. Check if it exists and is a directory.
/* Search the cache */
if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */
- if (ret->d_fullpath == NULL) { /* 2b */
+ if (ret->d_flags & DIRF_ISFILE) { /* 2b */
afp_errno = AFPERR_BADTYPE;
ret = NULL;
goto exit;
/* stat it and check if it's a dir */
LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstr(fullpath));
- if (stat(cfrombstr(fullpath), &st) != 0) { /* 5a */
+ if (lstat(cfrombstr(fullpath), &st) != 0) { /* 5a */
switch (errno) {
case ENOENT:
afp_errno = AFPERR_NOOBJ;
}
/* Create struct dir */
- if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */
+ if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, &st)) == NULL) { /* 6 */
LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
err = 1;
goto exit;
* @param vol (r) pointer to struct vol
* @param pdid (r) Parent CNID
* @param did (r) CNID
- * @param path (r) Full unix path to dir or NULL for files
- * @param ctime (r) st_ctime from stat
+ * @param path (r) Full unix path to object
+ * @param st (r) struct stat of object
*
* @returns pointer to new struct dir or NULL on error
*
cnid_t pdid,
cnid_t did,
bstring path,
- time_t ctime)
+ struct stat *st)
{
struct dir *dir;
dir->d_pdid = pdid;
dir->d_vid = vol->v_vid;
dir->d_fullpath = path;
- dir->ctime_dircache = ctime;
+ dir->dcache_ctime = st->st_ctime;
+ dir->dcache_ino = st->st_ino;
+ if (!S_ISDIR(st->st_mode))
+ dir->d_flags = DIRF_ISFILE;
return dir;
}
}
/* Allocate and initialize struct dir */
- if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath, path->st.st_ctime)) == NULL) { /* 3 */
+ if ((cdir = dir_new(path->m_name,
+ path->u_name,
+ vol,
+ dir->d_did,
+ id,
+ fullpath,
+ &path->st)) == NULL) { /* 3 */
err = 4;
goto exit;
}
#include "globals.h"
#include "volume.h"
-#define DIRF_FSMASK (3<<0)
-#define DIRF_NOFS (0<<0)
-#define DIRF_AFS (1<<0)
-#define DIRF_UFS (2<<0)
-
-#define DIRF_OFFCNT (1<<4) /* offsprings count is valid */
-#define DIRF_CNID (1<<5) /* renumerate id */
-
-#define AFPDIR_READ (1<<0)
-
/* directory bits */
#define DIRPBIT_ATTR 0
#define DIRPBIT_PDID 1
extern void dir_free_invalid_q(void);
extern struct dir *dir_new(const char *mname, const char *uname, const struct vol *,
- cnid_t pdid, cnid_t did, bstring fullpath, time_t ctime);
+ cnid_t pdid, cnid_t did, bstring fullpath, struct stat *);
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,
|| (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
|| (bitmap & (1 << FILPBIT_FNUM))) {
if (!path->id) {
+ bstring fullpath;
struct dir *cachedfile;
int len = strlen(upath);
if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
if (path->m_name == NULL) {
if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
LOG(log_error, logtype_afpd, "getmetadata: utompath error");
- exit(EXITERR_SYS);
+ return AFPERR_MISC;
}
}
- if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
+ /* Build fullpath */
+ if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
+ || (bconchar(fullpath, '/') != BSTR_OK)
+ || (bcatcstr(fullpath, upath)) != BSTR_OK) {
+ LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
+ return AFPERR_MISC;
+ }
+
+ if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
- exit(EXITERR_SYS);
+ return AFPERR_MISC;
}
if ((dircache_add(vol, cachedfile)) != 0) {
LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
- exit(EXITERR_SYS);
+ return AFPERR_MISC;
}
}
} else {
DIRDID_ROOT_PARENT,
DIRDID_ROOT,
bfromcstr(volume->v_path),
- st.st_ctime)
+ &st)
) == NULL) {
free(vol_mname);
LOG(log_error, logtype_afpd, "afp_openvol(%s): malloc: %s", volume->v_path, strerror(errno) );
#define DIRDID_ROOT_PARENT htonl(1) /* parent directory of root */
#define DIRDID_ROOT htonl(2) /* root directory */
+/* struct dir.d_flags */
+#define DIRF_FSMASK (3<<0)
+#define DIRF_NOFS (0<<0)
+#define DIRF_AFS (1<<0)
+#define DIRF_UFS (1<<1)
+#define DIRF_ISFILE (1<<3) /* it's cached file, not a directory */
+#define DIRF_OFFCNT (1<<4) /* offsprings count is valid */
+#define DIRF_CNID (1<<5) /* renumerate id */
+
struct dir {
- bstring d_fullpath; /* complete unix path to dir */
+ bstring d_fullpath; /* complete unix path to dir (or file) */
bstring d_m_name; /* mac name */
bstring d_u_name; /* unix name */
/* be careful here! if d_m_name == d_u_name, d_u_name */
uint16_t d_vid; /* only needed in the dircache, because
we put all directories in one cache. */
/* Stuff used in the dircache */
- time_t ctime_dircache; /* inode ctime, used and modified by dircache */
+ time_t dcache_ctime; /* inode ctime, used and modified by dircache */
+ ino_t dcache_ino; /* inode number, used to detect changes in the dircache */
};
struct path {