2 * $Id: dbd_lookup.c,v 1.11 2009-11-25 14:59:15 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
67 ----------------------
76 Possible fixup solution:
81 emacs uses a backup file (file~). When saving because of inode reusage of the fs,
82 both files exchange inodes.
84 General case for inode reusage:
87 16 2 f 1 1 new_file_with_reused_inode
96 --> this would be nice:
99 --> but because we must follow the general case you get this:
103 Result in dbd_lookup for the emacs case:
104 + devino --> CNID: 16
105 + didname -> CNID: 15
106 devino search and didname search result in different CNIDs !!
108 Possible fixup solution:
109 to be safe we must implement the general case, sorry emacs.
114 #endif /* HAVE_CONFIG_H */
119 #include <sys/param.h>
121 #include <netatalk/endian.h>
122 #include <atalk/logger.h>
123 #include <atalk/cnid_dbd_private.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, int roflag)
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 buf = pack_cnid_data(rqst);
154 /* Look for a CNID. We have two options: dev/ino or did/name. If we
155 only get a match in one of them, that means a file has moved. */
156 key.data = buf + CNID_DEVINO_OFS;
157 key.size = CNID_DEVINO_LEN;
159 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
160 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
161 ntohl(rqst->did), rqst->name);
162 rply->result = CNID_DBD_RES_ERR_DB;
169 memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
170 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
171 type_devino = ntohl(type_devino);
174 key.data = buf + CNID_DID_OFS;
175 key.size = CNID_DID_LEN + rqst->namelen + 1;
177 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
178 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
179 ntohl(rqst->did), rqst->name);
180 rply->result = CNID_DBD_RES_ERR_DB;
187 memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
188 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
189 type_didname = ntohl(type_didname);
192 /* Have we found anything at all ? */
193 if (!devino && !didname) {
195 LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database",
196 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
197 rply->result = CNID_DBD_RES_NOTFOUND;
201 /* Check for type (file/dir) mismatch */
202 if (devino && (type_devino != rqst->type)) {
203 /* one is a dir one is a file, remove from db */
204 rqst->cnid = id_devino;
206 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
208 rply->result = CNID_DBD_RES_NOTFOUND;
210 } else if (didname && (type_didname != rqst->type)) {
211 /* same: one is a dir one is a file, remove from db */
212 rqst->cnid = id_didname;
214 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
216 rply->result = CNID_DBD_RES_NOTFOUND;
220 if (devino && didname && id_devino == id_didname) {
221 /* everything is fine */
222 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
223 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
224 rply->cnid = id_didname;
225 rply->result = CNID_DBD_RES_OK;
229 if (devino && didname && id_devino != id_didname) {
230 /* CNIDs don't match, something of a worst case! */
231 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
232 ntohl(rqst->did), rqst->name, ntohl(id_didname),
233 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
235 /* Something like 5), the emacs case (see above), remove it all */
237 rqst->cnid = id_devino;
238 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
241 rqst->cnid = id_didname;
242 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
245 rply->result = CNID_DBD_RES_NOTFOUND;
250 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode",
251 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
252 rqst->cnid = id_devino;
254 if (strcmp(rqst->name, (char *)devdata.data + CNID_NAME_OFS) == 0) {
255 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv from one dir to another");
259 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
261 rply->result = CNID_DBD_RES_NOTFOUND;
267 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
268 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
269 rqst->cnid = id_didname;
273 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
274 if (!update || roflag) {
275 rply->result = CNID_DBD_RES_NOTFOUND;
279 /* Fix up the database */
280 rc = dbd_update(dbd, rqst, rply);
282 rply->cnid = rqst->cnid;
285 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u",
286 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));