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 <arpa/inet.h>
121 #include <atalk/logger.h>
122 #include <atalk/cnid_dbd_private.h>
129 * This returns the CNID corresponding to a particular file. It will also fix
130 * up the database if there's a problem.
133 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
136 DBT key, devdata, diddata;
137 int devino = 1, didname = 1;
139 cnid_t id_devino, id_didname;
140 u_int32_t type_devino = (unsigned)-1;
141 u_int32_t type_didname = (unsigned)-1;
144 memset(&key, 0, sizeof(key));
145 memset(&diddata, 0, sizeof(diddata));
146 memset(&devdata, 0, sizeof(devdata));
151 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START");
153 buf = pack_cnid_data(rqst);
155 /* Look for a CNID. We have two options: dev/ino or did/name. If we
156 only get a match in one of them, that means a file has moved. */
157 key.data = buf + CNID_DEVINO_OFS;
158 key.size = CNID_DEVINO_LEN;
160 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
161 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
162 ntohl(rqst->did), rqst->name);
163 rply->result = CNID_DBD_RES_ERR_DB;
170 memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
171 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
172 type_devino = ntohl(type_devino);
175 key.data = buf + CNID_DID_OFS;
176 key.size = CNID_DID_LEN + rqst->namelen + 1;
178 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
179 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
180 ntohl(rqst->did), rqst->name);
181 rply->result = CNID_DBD_RES_ERR_DB;
188 memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
189 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
190 type_didname = ntohl(type_didname);
193 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}",
194 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname);
196 /* Have we found anything at all ? */
197 if (!devino && !didname) {
199 LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database",
200 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
201 rply->result = CNID_DBD_RES_NOTFOUND;
205 /* Check for type (file/dir) mismatch */
206 if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) {
208 if (devino && (type_devino != rqst->type)) {
209 /* one is a dir one is a file, remove from db */
211 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino",
212 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
215 rqst->cnid = id_devino;
216 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
217 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
218 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
220 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino",
221 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
227 if (didname && (type_didname != rqst->type)) {
228 /* same: one is a dir one is a file, remove from db */
230 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname",
231 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
234 rqst->cnid = id_didname;
235 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
236 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
237 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
239 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname",
240 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
246 rply->result = CNID_DBD_RES_NOTFOUND;
250 if (devino && didname && id_devino == id_didname) {
251 /* everything is fine */
252 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
253 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
254 rply->cnid = id_didname;
255 rply->result = CNID_DBD_RES_OK;
259 if (devino && didname && id_devino != id_didname) {
260 /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
261 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
262 ntohl(rqst->did), rqst->name, ntohl(id_didname),
263 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
266 rqst->cnid = id_devino;
267 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
270 rqst->cnid = id_didname;
271 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
274 rply->result = CNID_DBD_RES_NOTFOUND;
279 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",
280 ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
281 if (rqst->cnid == id_devino) {
282 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
286 rqst->cnid = id_devino;
287 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
290 rply->result = CNID_DBD_RES_NOTFOUND;
296 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
297 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
299 rqst->cnid = id_didname;
300 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
303 rply->result = CNID_DBD_RES_NOTFOUND;
307 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
308 if (!update || roflag) {
309 rply->result = CNID_DBD_RES_NOTFOUND;
313 /* Fix up the database */
314 rc = dbd_update(dbd, rqst, rply);
316 rply->cnid = rqst->cnid;
319 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u",
320 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));