2 * $Id: dbd_lookup.c,v 1.10 2009-11-24 12:01:04 franklahm Exp $
4 * Copyright (C) Joerg Lenneis 2003
5 * Copyright (C) Frank Lahm 2009
6 * All Rights Reserved. See COPYING.
11 whatever you change here, also change cmd_dbd_lookup.c !
12 cmd_dbd_lookup has an read-only mode but besides that it's the same.
18 Id Did T Dev Inode Name
19 ============================
24 ...are the expected results of certain operations.
29 Name is changed but inode stays the same. We should try to keep the CNID.
35 Result in dbd_lookup (-: not found, +: found):
45 Changed inode and name. Result is just a new file which will get a fresh CNID.
46 Unfortunately the old one gets orphaned.
56 Possible fixup solution:
57 Not possible. Only dbd -re can delete the orphaned CNID 15
60 3) Restore from backup
61 ----------------------
70 Possible fixup solution:
74 4) inode reusage eg. UNIX emacs
75 -------------------------------
77 emacs uses a backup file (file~). When saving because of inode reusage of the fs,
78 both files exchange inodes. There's probably no appropiate solution for all
79 scenarios where this might occur eg. for the emacs case it would be good to
80 preserve the CNIDs while probably in the general case we should assign new CNIDs
86 16 2 f 1 1 new_file_with_reused_inode
99 Possible fixup solution:
100 to be safe we must implement the general case, sorry emacs.
105 #endif /* HAVE_CONFIG_H */
110 #include <sys/param.h>
112 #include <netatalk/endian.h>
113 #include <atalk/logger.h>
114 #include <atalk/cnid_dbd_private.h>
121 * This returns the CNID corresponding to a particular file. It will also fix
122 * up the database if there's a problem.
125 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
128 DBT key, devdata, diddata;
129 char dev[CNID_DEV_LEN];
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;
138 memset(&key, 0, sizeof(key));
139 memset(&diddata, 0, sizeof(diddata));
140 memset(&devdata, 0, sizeof(devdata));
145 buf = pack_cnid_data(rqst);
146 memcpy(dev, buf + CNID_DEV_OFS, CNID_DEV_LEN);
148 /* Look for a CNID. We have two options: dev/ino or did/name. If we
149 only get a match in one of them, that means a file has moved. */
150 key.data = buf + CNID_DEVINO_OFS;
151 key.size = CNID_DEVINO_LEN;
153 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) {
154 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
155 ntohl(rqst->did), rqst->name);
156 rply->result = CNID_DBD_RES_ERR_DB;
163 memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
164 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
165 type_devino = ntohl(type_devino);
168 key.data = buf + CNID_DID_OFS;
169 key.size = CNID_DID_LEN + rqst->namelen + 1;
171 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) {
172 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
173 ntohl(rqst->did), rqst->name);
174 rply->result = CNID_DBD_RES_ERR_DB;
181 memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
182 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
183 type_didname = ntohl(type_didname);
186 if (!devino && !didname) {
189 LOG(log_debug, logtype_cnid, "cnid_lookup: dev/ino 0x%llx/0x%llx did %u name %s neither in devino nor didname",
190 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name);
192 rply->result = CNID_DBD_RES_NOTFOUND;
196 if (devino && didname && id_devino == id_didname && type_devino == rqst->type) {
199 LOG(log_debug, logtype_cnid, "cnid_lookup: Looked up dev/ino 0x%llx/0x%llx did %u name %s as %u",
200 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name, ntohl(id_didname));
202 rply->cnid = id_didname;
203 rply->result = CNID_DBD_RES_OK;
208 Order matters for the next 2 ifs because both found a CNID but they do not match.
209 So in order to pick the CNID from "didname" it must come after devino.
210 See test cases laid out in dbd_lookup.c.
213 LOG(log_maxdebug, logtype_cnid, "CNID resolve problem: server side rename oder reused inode for '%s'", rqst->name);
214 rqst->cnid = id_devino;
215 if (type_devino != rqst->type) {
216 /* same dev:inode but not same type one is a folder the other
217 * is a file,it's an inode reused, delete the record
219 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) {
228 LOG(log_maxdebug, logtype_cnid, "CNID resolve problem: changed dev/ino for '%s'", rqst->name);
229 rqst->cnid = id_didname;
230 /* we have a did:name
231 * if it's the same dev or not the same type
234 if (!memcmp(dev, (char *)diddata.data + CNID_DEV_OFS, CNID_DEV_LEN) ||
235 type_didname != rqst->type) {
236 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) {
245 rply->result = CNID_DBD_RES_NOTFOUND;
248 /* Fix up the database. assume it was a file move and rename */
249 rc = dbd_update(dbd, rqst, rply);
251 rply->cnid = rqst->cnid;
254 LOG(log_debug, logtype_cnid, "cnid_lookup: Looked up dev/ino 0x%llx/0x%llx did %u name %s as %u (needed update)",
255 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name, ntohl(rply->cnid));