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. In certain cases we can use this hint to determince
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>
123 #include <atalk/cnid.h>
130 * This returns the CNID corresponding to a particular file. It will also fix
131 * up the database if there's a problem.
134 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
137 DBT key, devdata, diddata;
138 int devino = 1, didname = 1;
140 cnid_t id_devino, id_didname;
141 u_int32_t type_devino = (unsigned)-1;
142 u_int32_t type_didname = (unsigned)-1;
145 memset(&key, 0, sizeof(key));
146 memset(&diddata, 0, sizeof(diddata));
147 memset(&devdata, 0, sizeof(devdata));
152 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START");
154 buf = pack_cnid_data(rqst);
156 /* Look for a CNID. We have two options: dev/ino or did/name. If we
157 only get a match in one of them, that means a file has moved. */
158 key.data = buf + CNID_DEVINO_OFS;
159 key.size = CNID_DEVINO_LEN;
161 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
162 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
163 ntohl(rqst->did), rqst->name);
164 rply->result = CNID_DBD_RES_ERR_DB;
171 memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
172 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
173 type_devino = ntohl(type_devino);
176 key.data = buf + CNID_DID_OFS;
177 key.size = CNID_DID_LEN + rqst->namelen + 1;
179 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
180 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
181 ntohl(rqst->did), rqst->name);
182 rply->result = CNID_DBD_RES_ERR_DB;
189 memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
190 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
191 type_didname = ntohl(type_didname);
194 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}",
195 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname);
197 /* Have we found anything at all ? */
198 if (!devino && !didname) {
200 LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database",
201 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
202 rply->result = CNID_DBD_RES_NOTFOUND;
206 /* Check for type (file/dir) mismatch */
207 if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) {
209 if (devino && (type_devino != rqst->type)) {
210 /* one is a dir one is a file, remove from db */
212 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino",
213 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);
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);
232 rqst->cnid = id_didname;
233 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
234 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
235 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
237 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname",
238 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
243 rply->result = CNID_DBD_RES_NOTFOUND;
247 if (devino && didname && id_devino == id_didname) {
248 /* everything is fine */
249 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
250 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
251 rply->cnid = id_didname;
252 rply->result = CNID_DBD_RES_OK;
256 if (devino && didname && id_devino != id_didname) {
257 /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
258 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
259 ntohl(rqst->did), rqst->name, ntohl(id_didname),
260 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
262 rqst->cnid = id_devino;
263 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
266 rqst->cnid = id_didname;
267 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
270 rply->result = CNID_DBD_RES_NOTFOUND;
275 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",
276 ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
277 if (rqst->cnid == id_devino) {
278 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
281 rqst->cnid = id_devino;
282 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
284 rply->result = CNID_DBD_RES_NOTFOUND;
285 rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
291 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
292 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
293 rqst->cnid = id_didname;
294 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
296 rply->result = CNID_DBD_RES_NOTFOUND;
297 rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
301 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
303 rply->result = CNID_DBD_RES_NOTFOUND;
307 /* Fix up the database */
308 rc = dbd_update(dbd, rqst, rply);
310 rply->cnid = rqst->cnid;
313 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u",
314 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));