2 * $Id: dbd_lookup.c,v 1.18 2010-01-19 14:57:11 franklahm Exp $
4 * Copyright (C) Joerg Lenneis 2003
5 * Copyright (C) Frank Lahm 2009
6 * All Rights Reserved. See COPYING.
11 general rule: better safe then sorry, so we always delete CNIDs and assign
12 new ones in case of a lookup mismatch. afpd also sends us the CNID found
13 in the adouble file (for AFPVOL_CACHE volumes). In certain cases we can
14 use this hint to determince the right CNID.
19 Id Did T Dev Inode Name
20 ============================
25 ...are the expected results of certain operations. (f) is the speced CNID, in some
26 cases it's only intermediate as described in the text and is overridden by another
29 1) UNIX rename (via mv) or inode reusage(!)
30 -------------------------------------------
31 Name is possibly changed (rename case) but inode is the same.
32 We should try to keep the CNID, but we cant, because inode reusage is probably
38 15 x f 1 1 renamedfile
43 16 y f 1 1 inodereusagefile
45 Result in dbd_lookup (-: not found, +: found):
50 None. Delete old data, file gets new CNID in both cases (rename and inode).
51 If we got a hint and hint matches the CNID from devino we keep it and update
54 2) UNIX mv from one folder to another
55 ----------------------------------------
56 Name is unchanged and inode stays the same, but DID is different.
57 We should try to keep the CNID.
68 strcmp names, if they match keep CNID. Unfortunately this also can't be
69 distinguished from a new file with a reused inode. So me must assign
71 If we got a hint and hint matches the CNID from devino we keep it and update
74 3) Restore from backup ie change of inode number -- or emacs
75 ------------------------------------------------------------
85 Possible fixup solution:
86 test-suite test235 tests and ensures that the CNID is _changed_. The reason for
87 this is somewhat lost in time, but nevertheless we believe our test suite.
89 Similar things happen with emas: emacs uses a backup file (file~). When saving
90 because of inode reusage of the fs, both files most likely exchange inodes.
94 --> this would be nice:
97 --> but for the reasons described above we must implement
101 Result in dbd_lookup for the emacs case:
102 + devino --> CNID: 16
103 + didname -> CNID: 15
104 devino search and didname search result in different CNIDs !!
106 Possible fixup solution:
107 to be safe we must assign new CNIDs to both files.
112 #endif /* HAVE_CONFIG_H */
117 #include <sys/param.h>
119 #include <netatalk/endian.h>
120 #include <atalk/logger.h>
121 #include <atalk/cnid_dbd_private.h>
128 * This returns the CNID corresponding to a particular file. It will also fix
129 * up the database if there's a problem.
132 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
135 DBT key, devdata, diddata;
136 int devino = 1, didname = 1;
138 cnid_t id_devino, id_didname;
139 u_int32_t type_devino = (unsigned)-1;
140 u_int32_t type_didname = (unsigned)-1;
143 memset(&key, 0, sizeof(key));
144 memset(&diddata, 0, sizeof(diddata));
145 memset(&devdata, 0, sizeof(devdata));
150 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START");
152 buf = pack_cnid_data(rqst);
154 /* Look for a CNID. We have two options: dev/ino or did/name. If we
155 only get a match in one of them, that means a file has moved. */
156 key.data = buf + CNID_DEVINO_OFS;
157 key.size = CNID_DEVINO_LEN;
159 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
160 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
161 ntohl(rqst->did), rqst->name);
162 rply->result = CNID_DBD_RES_ERR_DB;
169 memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
170 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
171 type_devino = ntohl(type_devino);
174 key.data = buf + CNID_DID_OFS;
175 key.size = CNID_DID_LEN + rqst->namelen + 1;
177 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
178 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
179 ntohl(rqst->did), rqst->name);
180 rply->result = CNID_DBD_RES_ERR_DB;
187 memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
188 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
189 type_didname = ntohl(type_didname);
192 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}",
193 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname);
195 /* Have we found anything at all ? */
196 if (!devino && !didname) {
198 LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database",
199 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
200 rply->result = CNID_DBD_RES_NOTFOUND;
204 /* Check for type (file/dir) mismatch */
205 if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) {
207 if (devino && (type_devino != rqst->type)) {
208 /* one is a dir one is a file, remove from db */
210 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino",
211 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
214 rqst->cnid = id_devino;
215 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
216 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
217 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
219 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino",
220 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
226 if (didname && (type_didname != rqst->type)) {
227 /* same: one is a dir one is a file, remove from db */
229 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname",
230 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
233 rqst->cnid = id_didname;
234 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
235 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
236 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
238 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname",
239 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
245 rply->result = CNID_DBD_RES_NOTFOUND;
249 if (devino && didname && id_devino == id_didname) {
250 /* everything is fine */
251 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
252 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
253 rply->cnid = id_didname;
254 rply->result = CNID_DBD_RES_OK;
258 if (devino && didname && id_devino != id_didname) {
259 /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
260 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
261 ntohl(rqst->did), rqst->name, ntohl(id_didname),
262 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
265 rqst->cnid = id_devino;
266 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
269 rqst->cnid = id_didname;
270 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
273 rply->result = CNID_DBD_RES_NOTFOUND;
278 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",
279 ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
280 if (rqst->cnid == id_devino) {
281 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
285 rqst->cnid = id_devino;
286 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
289 rply->result = CNID_DBD_RES_NOTFOUND;
295 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
296 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
298 rqst->cnid = id_didname;
299 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
302 rply->result = CNID_DBD_RES_NOTFOUND;
306 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
307 if (!update || roflag) {
308 rply->result = CNID_DBD_RES_NOTFOUND;
312 /* Fix up the database */
313 rc = dbd_update(dbd, rqst, rply);
315 rply->cnid = rqst->cnid;
318 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u",
319 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));