X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fcnid_dbd%2Fdbd_lookup.c;h=9ff4e411d356115829d7f3b6959168884d367047;hb=f254fd618b53e97cc5382b23709d4f3de1e70b41;hp=7ae9c1fe9f66b35e99c18907d675f4f06f731fec;hpb=ecfc96169ab669b578e53fa8e13592934fe37788;p=netatalk.git diff --git a/etc/cnid_dbd/dbd_lookup.c b/etc/cnid_dbd/dbd_lookup.c index 7ae9c1fe..9ff4e411 100644 --- a/etc/cnid_dbd/dbd_lookup.c +++ b/etc/cnid_dbd/dbd_lookup.c @@ -1,10 +1,111 @@ /* - * $Id: dbd_lookup.c,v 1.2 2005-04-28 20:49:48 bfernhomberg Exp $ * * Copyright (C) Joerg Lenneis 2003 + * Copyright (C) Frank Lahm 2009 * All Rights Reserved. See COPYING. */ +/* +CNID salvation spec: +general rule: better safe then sorry, so we always delete CNIDs and assign +new ones in case of a lookup mismatch. afpd also sends us the CNID found +in the adouble file. In certain cases we can use this hint to determince +the right CNID. + + +The lines... + +Id Did T Dev Inode Name +============================ +a b c d e name1 +--> +f g h i h name2 + +...are the expected results of certain operations. (f) is the speced CNID, in some +cases it's only intermediate as described in the text and is overridden by another +spec. + +1) UNIX rename (via mv) or inode reusage(!) +------------------------------------------- +Name is possibly changed (rename case) but inode is the same. +We should try to keep the CNID, but we cant, because inode reusage is probably +much to frequent. + +rename: +15 2 f 1 1 file +--> +15 x f 1 1 renamedfile + +inode reusage: +15 2 f 1 1 file +--> +16 y f 1 1 inodereusagefile + +Result in dbd_lookup (-: not found, +: found): ++ devino +- didname + +Possible solution: +None. Delete old data, file gets new CNID in both cases (rename and inode). +If we got a hint and hint matches the CNID from devino we keep it and update +the record. + +2) UNIX mv from one folder to another +---------------------------------------- +Name is unchanged and inode stays the same, but DID is different. +We should try to keep the CNID. + +15 2 f 1 1 file +--> +15 x f 1 1 file + +Result in dbd_lookup: ++ devino +- didname + +Possible solution: +strcmp names, if they match keep CNID. Unfortunately this also can't be +distinguished from a new file with a reused inode. So me must assign +a new CNID. +If we got a hint and hint matches the CNID from devino we keep it and update +the record. + +3) Restore from backup ie change of inode number -- or emacs +------------------------------------------------------------ + +15 2 f 1 1 file +--> +15 2 f 1 2 file + +Result in dbd_lookup: +- devino ++ didname + +Possible fixup solution: +test-suite test235 tests and ensures that the CNID is _changed_. The reason for +this is somewhat lost in time, but nevertheless we believe our test suite. + +Similar things happen with emas: emacs uses a backup file (file~). When saving +because of inode reusage of the fs, both files most likely exchange inodes. + +15 2 f 1 1 file +16 2 f 1 2 file~ +--> this would be nice: +15 2 f 1 2 file +16 2 f 1 1 file~ +--> but for the reasons described above we must implement +17 2 f 1 2 file +18 2 f 1 1 file~ + +Result in dbd_lookup for the emacs case: ++ devino --> CNID: 16 ++ didname -> CNID: 15 +devino search and didname search result in different CNIDs !! + +Possible fixup solution: +to be safe we must assign new CNIDs to both files. +*/ + #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ @@ -14,9 +115,11 @@ #include #include #include -#include +#include + #include -#include +#include +#include #include "pack.h" #include "dbif.h" @@ -27,12 +130,10 @@ * up the database if there's a problem. */ -int dbd_lookup(struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply) +int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply) { - char *buf; + unsigned char *buf; DBT key, devdata, diddata; - char dev[CNID_DEV_LEN]; - char ino[CNID_INO_LEN]; int devino = 1, didname = 1; int rc; cnid_t id_devino, id_didname; @@ -40,27 +141,25 @@ int dbd_lookup(struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply) u_int32_t type_didname = (unsigned)-1; int update = 0; - memset(&key, 0, sizeof(key)); memset(&diddata, 0, sizeof(diddata)); memset(&devdata, 0, sizeof(devdata)); rply->namelen = 0; rply->cnid = 0; + + LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START"); buf = pack_cnid_data(rqst); - memcpy(dev, buf + CNID_DEV_OFS, CNID_DEV_LEN); - /* FIXME: ino is not needed later on, remove? */ - memcpy(ino, buf + CNID_INO_OFS, CNID_INO_LEN); /* Look for a CNID. We have two options: dev/ino or did/name. If we only get a match in one of them, that means a file has moved. */ - key.data = buf +CNID_DEVINO_OFS; + key.data = buf + CNID_DEVINO_OFS; key.size = CNID_DEVINO_LEN; - if ((rc = dbif_get(DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) { + if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) { LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s", - ntohl(rqst->did), rqst->name); + ntohl(rqst->did), rqst->name); rply->result = CNID_DBD_RES_ERR_DB; return -1; } @@ -72,15 +171,13 @@ int dbd_lookup(struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply) memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino)); type_devino = ntohl(type_devino); } - - /* FIXME: This second call to pack_cnid_data() is redundant, any reason it is here? */ - buf = pack_cnid_data(rqst); - key.data = buf +CNID_DID_OFS; + + key.data = buf + CNID_DID_OFS; key.size = CNID_DID_LEN + rqst->namelen + 1; - if ((rc = dbif_get(DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) { + if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) { LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s", - ntohl(rqst->did), rqst->name); + ntohl(rqst->did), rqst->name); rply->result = CNID_DBD_RES_ERR_DB; return -1; } @@ -92,71 +189,128 @@ int dbd_lookup(struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply) memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname)); type_didname = ntohl(type_didname); } - + + LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}", + rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname); + + /* Have we found anything at all ? */ if (!devino && !didname) { - /* not found */ -#ifdef DEBUG - LOG(log_info, logtype_cnid, "cnid_lookup: dev/ino %s did %u name %s neither in devino nor didname", - stringify_devino(rqst->dev, rqst->ino), ntohl(rqst->did), rqst->name); -#endif + /* nothing found */ + LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database", + rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); rply->result = CNID_DBD_RES_NOTFOUND; return 1; } - if (devino && didname && id_devino == id_didname && type_devino == rqst->type) { - /* the same */ -#ifdef DEBUG - LOG(log_info, logtype_cnid, "cnid_lookup: Looked up dev/ino %s did %u name %s as %u", - stringify_devino(rqst->dev, rqst->ino), ntohl(rqst->did), rqst->name, ntohl(id_didname)); -#endif - rply->cnid = id_didname; - rply->result = CNID_DBD_RES_OK; - return 1; - } - - if (didname) { - rqst->cnid = id_didname; - /* we have a did:name - * if it's the same dev or not the same type - * just delete it - */ - if (!memcmp(dev, (char *)diddata.data + CNID_DEV_OFS, CNID_DEV_LEN) || - type_didname != rqst->type) { - if (dbd_delete(rqst, rply) < 0) { + /* Check for type (file/dir) mismatch */ + if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) { + + if (devino && (type_devino != rqst->type)) { + /* one is a dir one is a file, remove from db */ + + LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino", + rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); + + rqst->cnid = id_devino; + rc = dbd_delete(dbd, rqst, rply, DBIF_CNID); + rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO); + rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME); + if (rc < 0) { + LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", + rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); return -1; } } - else { - update = 1; - } - } - if (devino) { - rqst->cnid = id_devino; - if (type_devino != rqst->type) { - /* same dev:inode but not same type one is a folder the other - * is a file,it's an inode reused, delete the record - */ - if (dbd_delete(rqst, rply) < 0) { + if (didname && (type_didname != rqst->type)) { + /* same: one is a dir one is a file, remove from db */ + + LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname", + rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); + + rqst->cnid = id_didname; + rc = dbd_delete(dbd, rqst, rply, DBIF_CNID); + rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO); + rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME); + if (rc < 0) { + LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", + rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); return -1; } } - else { + + rply->result = CNID_DBD_RES_NOTFOUND; + return 1; + } + + if (devino && didname && id_devino == id_didname) { + /* everything is fine */ + LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u", + ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname)); + rply->cnid = id_didname; + rply->result = CNID_DBD_RES_OK; + return 1; + } + + if (devino && didname && id_devino != id_didname) { + /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */ + LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u", + ntohl(rqst->did), rqst->name, ntohl(id_didname), + (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino)); + + rqst->cnid = id_devino; + if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) + return -1; + + rqst->cnid = id_didname; + if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) + return -1; + + rply->result = CNID_DBD_RES_NOTFOUND; + return 1; + } + + if ( ! didname) { + LOG(log_debug, logtype_cnid, "dbd_lookup(CNID hint: %u, DID:%u, \"%s\", 0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode", + ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); + if (rqst->cnid == id_devino) { + LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)"); update = 1; + } else { + rqst->cnid = id_devino; + if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) + return -1; + rply->result = CNID_DBD_RES_NOTFOUND; + rqst->cnid = CNID_INVALID; /* invalidate CNID hint */ + return 1; } } + + if ( ! devino) { + LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino", + ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); + rqst->cnid = id_didname; + if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) + return -1; + rply->result = CNID_DBD_RES_NOTFOUND; + rqst->cnid = CNID_INVALID; /* invalidate CNID hint */ + return 1; + } + + /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/ if (!update) { rply->result = CNID_DBD_RES_NOTFOUND; return 1; } - /* Fix up the database. assume it was a file move and rename */ - rc = dbd_update(rqst, rply); + + /* Fix up the database */ + rc = dbd_update(dbd, rqst, rply); if (rc >0) { rply->cnid = rqst->cnid; } -#ifdef DEBUG - LOG(log_info, logtype_cnid, "cnid_lookup: Looked up dev/ino %s did %u name %s as %u (needed update)", - stringify_devino(rqst->dev, rqst->ino), ntohl(rqst->did), rqst->name, ntohl(rply->cnid)); -#endif + + LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u", + ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid)); + return rc; }