3 * Copyright (C) Joerg Lenneis 2003
4 * Copyright (C) Frank Lahm 2009
5 * All Rights Reserved. See COPYING.
10 general rule: better safe then sorry, so we always delete CNIDs and assign
11 new ones in case of a lookup mismatch. afpd also sends us the CNID found
12 in the adouble file. In certain cases we can use this hint to determince
18 Id Did T Dev Inode Name
19 ============================
24 ...are the expected results of certain operations. (f) is the speced CNID, in some
25 cases it's only intermediate as described in the text and is overridden by another
28 1) UNIX rename (via mv) or inode reusage(!)
29 -------------------------------------------
30 Name is possibly changed (rename case) but inode is the same.
31 We should try to keep the CNID, but we cant, because inode reusage is probably
37 15 x f 1 1 renamedfile
42 16 y f 1 1 inodereusagefile
44 Result in dbd_lookup (-: not found, +: found):
49 None. Delete old data, file gets new CNID in both cases (rename and inode).
50 If we got a hint and hint matches the CNID from devino we keep it and update
53 2) UNIX mv from one folder to another
54 ----------------------------------------
55 Name is unchanged and inode stays the same, but DID is different.
56 We should try to keep the CNID.
67 strcmp names, if they match keep CNID. Unfortunately this also can't be
68 distinguished from a new file with a reused inode. So me must assign
70 If we got a hint and hint matches the CNID from devino we keep it and update
73 3) Restore from backup ie change of inode number -- or emacs
74 ------------------------------------------------------------
84 Possible fixup solution:
85 test-suite test235 tests and ensures that the CNID is _changed_. The reason for
86 this is somewhat lost in time, but nevertheless we believe our test suite.
88 Similar things happen with emas: emacs uses a backup file (file~). When saving
89 because of inode reusage of the fs, both files most likely exchange inodes.
93 --> this would be nice:
96 --> but for the reasons described above we must implement
100 Result in dbd_lookup for the emacs case:
101 + devino --> CNID: 16
102 + didname -> CNID: 15
103 devino search and didname search result in different CNIDs !!
105 Possible fixup solution:
106 to be safe we must assign new CNIDs to both files.
111 #endif /* HAVE_CONFIG_H */
116 #include <sys/param.h>
118 #include <arpa/inet.h>
120 #include <atalk/logger.h>
121 #include <atalk/cnid_bdb_private.h>
122 #include <atalk/cnid.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)
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);
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);
225 if (didname && (type_didname != rqst->type)) {
226 /* same: one is a dir one is a file, remove from db */
228 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname",
229 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
231 rqst->cnid = id_didname;
232 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
233 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
234 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
236 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname",
237 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
242 rply->result = CNID_DBD_RES_NOTFOUND;
246 if (devino && didname && id_devino == id_didname) {
247 /* everything is fine */
248 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
249 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
250 rply->cnid = id_didname;
251 rply->result = CNID_DBD_RES_OK;
255 if (devino && didname && id_devino != id_didname) {
256 /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
257 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
258 ntohl(rqst->did), rqst->name, ntohl(id_didname),
259 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
261 rqst->cnid = id_devino;
262 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
265 rqst->cnid = id_didname;
266 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
269 rply->result = CNID_DBD_RES_NOTFOUND;
274 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",
275 ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
276 if (rqst->cnid == id_devino) {
277 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
280 rqst->cnid = id_devino;
281 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
283 rply->result = CNID_DBD_RES_NOTFOUND;
284 rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
290 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
291 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
292 rqst->cnid = id_didname;
293 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
295 rply->result = CNID_DBD_RES_NOTFOUND;
296 rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
300 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
302 rply->result = CNID_DBD_RES_NOTFOUND;
306 /* Fix up the database */
307 rc = dbd_update(dbd, rqst, rply);
309 rply->cnid = rqst->cnid;
312 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u",
313 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));