2 * $Id: dbd_lookup.c,v 1.12 2009-11-27 15:45:41 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.
66 3) Restore from backup ie change of inode number -- or emacs
67 ------------------------------------------------------------
77 Possible fixup solution:
78 test-suite test235 tests and ensures that the CNID is _changed_. The reason for
79 this is somewhat lost in time, but nevertheless we believe our test suite.
81 Similar things happen with emas: emacs uses a backup file (file~). When saving
82 because of inode reusage of the fs, both files most likely exchange inodes.
86 --> this would be nice:
89 --> but for the reasons described above we must implement
93 Result in dbd_lookup for the emacs case:
96 devino search and didname search result in different CNIDs !!
98 Possible fixup solution:
99 to be safe we must assign new CNIDs to both files.
104 #endif /* HAVE_CONFIG_H */
109 #include <sys/param.h>
111 #include <netatalk/endian.h>
112 #include <atalk/logger.h>
113 #include <atalk/cnid_dbd_private.h>
120 * This returns the CNID corresponding to a particular file. It will also fix
121 * up the database if there's a problem.
124 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
127 DBT key, devdata, diddata;
128 int devino = 1, didname = 1;
130 cnid_t id_devino, id_didname;
131 u_int32_t type_devino = (unsigned)-1;
132 u_int32_t type_didname = (unsigned)-1;
135 memset(&key, 0, sizeof(key));
136 memset(&diddata, 0, sizeof(diddata));
137 memset(&devdata, 0, sizeof(devdata));
142 buf = pack_cnid_data(rqst);
144 /* Look for a CNID. We have two options: dev/ino or did/name. If we
145 only get a match in one of them, that means a file has moved. */
146 key.data = buf + CNID_DEVINO_OFS;
147 key.size = CNID_DEVINO_LEN;
149 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
150 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
151 ntohl(rqst->did), rqst->name);
152 rply->result = CNID_DBD_RES_ERR_DB;
159 memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
160 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
161 type_devino = ntohl(type_devino);
164 key.data = buf + CNID_DID_OFS;
165 key.size = CNID_DID_LEN + rqst->namelen + 1;
167 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
168 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
169 ntohl(rqst->did), rqst->name);
170 rply->result = CNID_DBD_RES_ERR_DB;
177 memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
178 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
179 type_didname = ntohl(type_didname);
182 /* Have we found anything at all ? */
183 if (!devino && !didname) {
185 LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database",
186 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
187 rply->result = CNID_DBD_RES_NOTFOUND;
191 /* Check for type (file/dir) mismatch */
192 if (devino && (type_devino != rqst->type)) {
193 /* one is a dir one is a file, remove from db */
195 rqst->cnid = id_devino;
196 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
199 rply->result = CNID_DBD_RES_NOTFOUND;
201 } else if (didname && (type_didname != rqst->type)) {
202 /* same: one is a dir one is a file, remove from db */
204 rqst->cnid = id_didname;
205 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
208 rply->result = CNID_DBD_RES_NOTFOUND;
212 if (devino && didname && id_devino == id_didname) {
213 /* everything is fine */
214 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
215 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
216 rply->cnid = id_didname;
217 rply->result = CNID_DBD_RES_OK;
221 if (devino && didname && id_devino != id_didname) {
222 /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
223 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
224 ntohl(rqst->did), rqst->name, ntohl(id_didname),
225 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
228 rqst->cnid = id_devino;
229 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
232 rqst->cnid = id_didname;
233 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
236 rply->result = CNID_DBD_RES_NOTFOUND;
241 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode",
242 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
244 if (strcmp(rqst->name, (char *)devdata.data + CNID_NAME_OFS) == 0) {
245 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv from one dir to another");
249 rqst->cnid = id_devino;
250 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
253 rply->result = CNID_DBD_RES_NOTFOUND;
259 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
260 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
262 rqst->cnid = id_didname;
263 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
266 rply->result = CNID_DBD_RES_NOTFOUND;
270 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
271 if (!update || roflag) {
272 rply->result = CNID_DBD_RES_NOTFOUND;
276 /* Fix up the database */
277 rc = dbd_update(dbd, rqst, rply);
279 rply->cnid = rqst->cnid;
282 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u",
283 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));