2 * $Id: dbd_lookup.c,v 1.15 2009-12-08 10:26:12 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 assing
12 new ones in case of a lookup mismatch.
17 Id Did T Dev Inode Name
18 ============================
23 ...are the expected results of certain operations. (f) is the speced CNID, in some
24 cases it's only intermediate as described in the text and is overridden by another
27 1) UNIX rename (via mv) or inode reusage(!)
28 -------------------------------------------
29 Name is possibly changed (rename case) but inode is the same.
30 We should try to keep the CNID, but we cant, because inode reusage is probably
36 15 x f 1 1 renamedfile
41 16 y f 1 1 inodereusagefile
43 Result in dbd_lookup (-: not found, +: found):
48 None. Delete old data, file gets new CNID in both cases (rename and inode).
50 2) UNIX mv from one folder to another
51 ----------------------------------------
52 Name is unchanged and inode stays the same, but DID is different.
53 We should try to keep the CNID.
64 strcmp names, if they match keep CNID. Unfortunately this also can't be
65 distinguished from a new file with a reused inode. So me must assign
68 3) Restore from backup ie change of inode number -- or emacs
69 ------------------------------------------------------------
79 Possible fixup solution:
80 test-suite test235 tests and ensures that the CNID is _changed_. The reason for
81 this is somewhat lost in time, but nevertheless we believe our test suite.
83 Similar things happen with emas: emacs uses a backup file (file~). When saving
84 because of inode reusage of the fs, both files most likely exchange inodes.
88 --> this would be nice:
91 --> but for the reasons described above we must implement
95 Result in dbd_lookup for the emacs case:
98 devino search and didname search result in different CNIDs !!
100 Possible fixup solution:
101 to be safe we must assign new CNIDs to both files.
106 #endif /* HAVE_CONFIG_H */
111 #include <sys/param.h>
113 #include <netatalk/endian.h>
114 #include <atalk/logger.h>
115 #include <atalk/cnid_dbd_private.h>
122 * This returns the CNID corresponding to a particular file. It will also fix
123 * up the database if there's a problem.
126 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
129 DBT key, devdata, diddata;
130 int devino = 1, didname = 1;
132 cnid_t id_devino, id_didname;
133 u_int32_t type_devino = (unsigned)-1;
134 u_int32_t type_didname = (unsigned)-1;
137 memset(&key, 0, sizeof(key));
138 memset(&diddata, 0, sizeof(diddata));
139 memset(&devdata, 0, sizeof(devdata));
144 buf = pack_cnid_data(rqst);
146 /* Look for a CNID. We have two options: dev/ino or did/name. If we
147 only get a match in one of them, that means a file has moved. */
148 key.data = buf + CNID_DEVINO_OFS;
149 key.size = CNID_DEVINO_LEN;
151 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
152 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
153 ntohl(rqst->did), rqst->name);
154 rply->result = CNID_DBD_RES_ERR_DB;
161 memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
162 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
163 type_devino = ntohl(type_devino);
166 key.data = buf + CNID_DID_OFS;
167 key.size = CNID_DID_LEN + rqst->namelen + 1;
169 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
170 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
171 ntohl(rqst->did), rqst->name);
172 rply->result = CNID_DBD_RES_ERR_DB;
179 memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
180 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
181 type_didname = ntohl(type_didname);
184 /* Have we found anything at all ? */
185 if (!devino && !didname) {
187 LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database",
188 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
189 rply->result = CNID_DBD_RES_NOTFOUND;
193 /* Check for type (file/dir) mismatch */
194 if (devino && (type_devino != rqst->type)) {
195 /* one is a dir one is a file, remove from db */
197 rqst->cnid = id_devino;
198 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
201 rply->result = CNID_DBD_RES_NOTFOUND;
203 } else if (didname && (type_didname != rqst->type)) {
204 /* same: one is a dir one is a file, remove from db */
206 rqst->cnid = id_didname;
207 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
210 rply->result = CNID_DBD_RES_NOTFOUND;
214 if (devino && didname && id_devino == id_didname) {
215 /* everything is fine */
216 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
217 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
218 rply->cnid = id_didname;
219 rply->result = CNID_DBD_RES_OK;
223 if (devino && didname && id_devino != id_didname) {
224 /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
225 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
226 ntohl(rqst->did), rqst->name, ntohl(id_didname),
227 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
230 rqst->cnid = id_devino;
231 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
234 rqst->cnid = id_didname;
235 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
238 rply->result = CNID_DBD_RES_NOTFOUND;
243 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode",
244 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
245 if (rqst->cnid == id_devino) {
246 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
250 rqst->cnid = id_devino;
251 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
254 rply->result = CNID_DBD_RES_NOTFOUND;
260 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
261 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
263 rqst->cnid = id_didname;
264 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
267 rply->result = CNID_DBD_RES_NOTFOUND;
271 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
272 if (!update || roflag) {
273 rply->result = CNID_DBD_RES_NOTFOUND;
277 /* Fix up the database */
278 rc = dbd_update(dbd, rqst, rply);
280 rply->cnid = rqst->cnid;
283 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u",
284 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));