]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbd_lookup.c
44e46620adab03e1bbc88c88b2f31375a78c974a
[netatalk.git] / etc / cnid_dbd / dbd_lookup.c
1 /*
2  * $Id: dbd_lookup.c,v 1.18 2010-01-19 14:57:11 franklahm Exp $
3  *
4  * Copyright (C) Joerg Lenneis 2003
5  * Copyright (C) Frank Lahm 2009
6  * All Rights Reserved.  See COPYING.
7  */
8
9 /* 
10 CNID salvation spec:
11 general rule: better safe then sorry, so we always delete CNIDs and assign
12 new ones in case of a lookup mismatch. afpd also sends us the CNID found
13 in the adouble file. In certain cases we can use this hint to determince
14 the right CNID.
15
16
17 The lines...
18
19 Id  Did T   Dev Inode   Name
20 ============================
21 a   b   c   d   e       name1
22 -->
23 f   g   h   i   h       name2
24
25 ...are the expected results of certain operations. (f) is the speced CNID, in some
26 cases it's only intermediate as described in the text and is overridden by another
27 spec.
28
29 1) UNIX rename (via mv) or inode reusage(!)
30 -------------------------------------------
31 Name is possibly changed (rename case) but inode is the same.
32 We should try to keep the CNID, but we cant, because inode reusage is probably
33 much to frequent.
34
35 rename:
36 15  2   f   1   1       file
37 -->
38 15  x   f   1   1       renamedfile
39
40 inode reusage:
41 15  2   f   1   1       file
42 -->
43 16  y   f   1   1       inodereusagefile
44
45 Result in dbd_lookup (-: not found, +: found):
46 + devino
47 - didname
48
49 Possible solution:
50 None. Delete old data, file gets new CNID in both cases (rename and inode).
51 If we got a hint and hint matches the CNID from devino we keep it and update
52 the record.
53
54 2) UNIX mv from one folder to another
55 ----------------------------------------
56 Name is unchanged and inode stays the same, but DID is different.
57 We should try to keep the CNID.
58
59 15  2   f   1   1       file
60 -->
61 15  x   f   1   1       file
62
63 Result in dbd_lookup:
64 + devino
65 - didname
66
67 Possible solution:
68 strcmp names, if they match keep CNID. Unfortunately this also can't be
69 distinguished from a new file with a reused inode. So me must assign
70 a new CNID.
71 If we got a hint and hint matches the CNID from devino we keep it and update
72 the record.
73
74 3) Restore from backup ie change of inode number -- or emacs
75 ------------------------------------------------------------
76
77 15  2   f   1   1       file
78 -->
79 15  2   f   1   2       file
80
81 Result in dbd_lookup:
82 - devino
83 + didname
84
85 Possible fixup solution:
86 test-suite test235 tests and ensures that the CNID is _changed_. The reason for
87 this is somewhat lost in time, but nevertheless we believe our test suite.
88
89 Similar things happen with emas: emacs uses a backup file (file~). When saving
90 because of inode reusage of the fs, both files most likely exchange inodes.
91
92 15  2   f   1   1       file
93 16  2   f   1   2       file~
94 --> this would be nice:
95 15  2   f   1   2       file
96 16  2   f   1   1       file~
97 --> but for the reasons described above we must implement
98 17  2   f   1   2       file
99 18  2   f   1   1       file~
100
101 Result in dbd_lookup for the emacs case:
102 + devino --> CNID: 16
103 + didname -> CNID: 15
104 devino search and didname search result in different CNIDs !!
105
106 Possible fixup solution:
107 to be safe we must assign new CNIDs to both files.
108 */
109
110 #ifdef HAVE_CONFIG_H
111 #include "config.h"
112 #endif /* HAVE_CONFIG_H */
113
114
115 #include <stdio.h>
116 #include <string.h>
117 #include <sys/param.h>
118 #include <errno.h>
119 #include <arpa/inet.h>
120
121 #include <atalk/logger.h>
122 #include <atalk/cnid_dbd_private.h>
123 #include <atalk/cnid.h>
124
125 #include "pack.h"
126 #include "dbif.h"
127 #include "dbd.h"
128
129 /*
130  *  This returns the CNID corresponding to a particular file.  It will also fix
131  *  up the database if there's a problem.
132  */
133
134 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
135 {
136     unsigned char *buf;
137     DBT key, devdata, diddata;
138     int devino = 1, didname = 1; 
139     int rc;
140     cnid_t id_devino, id_didname;
141     u_int32_t type_devino  = (unsigned)-1;
142     u_int32_t type_didname = (unsigned)-1;
143     int update = 0;
144     
145     memset(&key, 0, sizeof(key));
146     memset(&diddata, 0, sizeof(diddata));
147     memset(&devdata, 0, sizeof(devdata));
148
149     rply->namelen = 0;
150     rply->cnid = 0;
151
152     LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START");
153     
154     buf = pack_cnid_data(rqst); 
155
156     /* Look for a CNID.  We have two options: dev/ino or did/name.  If we
157        only get a match in one of them, that means a file has moved. */
158     key.data = buf + CNID_DEVINO_OFS;
159     key.size = CNID_DEVINO_LEN;
160
161     if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0))  < 0) {
162         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
163             ntohl(rqst->did), rqst->name);
164         rply->result = CNID_DBD_RES_ERR_DB;
165         return -1;
166     }
167     if (rc == 0) {
168         devino = 0;
169     }
170     else {
171         memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
172         memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
173         type_devino = ntohl(type_devino);
174     }
175
176     key.data = buf + CNID_DID_OFS;
177     key.size = CNID_DID_LEN + rqst->namelen + 1;
178
179     if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0))  < 0) {
180         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
181             ntohl(rqst->did), rqst->name);
182         rply->result = CNID_DBD_RES_ERR_DB;
183         return -1;
184     }
185     if (rc == 0) {
186         didname = 0;
187     }
188     else {
189         memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
190         memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
191         type_didname = ntohl(type_didname);
192     }
193
194     LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}", 
195         rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname);
196
197     /* Have we found anything at all ? */
198     if (!devino && !didname) {  
199         /* nothing found */
200         LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database", 
201             rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
202         rply->result = CNID_DBD_RES_NOTFOUND;
203         return 1;
204     }
205
206     /* Check for type (file/dir) mismatch */
207     if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) {
208
209         if (devino && (type_devino != rqst->type)) {
210             /* one is a dir one is a file, remove from db */
211
212             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino", 
213                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
214
215             rqst->cnid = id_devino;
216             rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
217             rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
218             rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
219             if (rc < 0) {
220                 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 
221                     rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
222                 return -1;
223             }
224         }
225
226         if (didname && (type_didname != rqst->type)) {
227             /* same: one is a dir one is a file, remove from db */
228
229             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname", 
230                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
231
232             rqst->cnid = id_didname;
233             rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
234             rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
235             rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
236             if (rc < 0) {
237                 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
238                     rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
239                 return -1;
240             }
241         }
242
243         rply->result = CNID_DBD_RES_NOTFOUND;
244         return 1;
245     }
246
247     if (devino && didname && id_devino == id_didname) {
248         /* everything is fine */
249         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
250             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
251         rply->cnid = id_didname;
252         rply->result = CNID_DBD_RES_OK;
253         return 1;
254     }
255
256     if (devino && didname && id_devino != id_didname) {
257         /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
258         LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
259             ntohl(rqst->did), rqst->name, ntohl(id_didname),
260             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
261
262         rqst->cnid = id_devino;
263         if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
264             return -1;
265
266         rqst->cnid = id_didname;
267         if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
268             return -1;
269
270         rply->result = CNID_DBD_RES_NOTFOUND;
271         return 1;
272     }
273
274     if ( ! didname) {
275         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",
276             ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
277         if (rqst->cnid == id_devino) {
278             LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
279             update = 1;
280         } else {
281             rqst->cnid = id_devino;
282             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
283                 return -1;
284             rply->result = CNID_DBD_RES_NOTFOUND;
285             rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
286             return 1;
287         }
288     }
289
290     if ( ! devino) {
291         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
292             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
293         rqst->cnid = id_didname;
294         if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
295             return -1;
296         rply->result = CNID_DBD_RES_NOTFOUND;
297         rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
298         return 1;
299     }
300
301     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
302     if (!update) {
303         rply->result = CNID_DBD_RES_NOTFOUND;
304         return 1;
305     }
306
307     /* Fix up the database */
308     rc = dbd_update(dbd, rqst, rply);
309     if (rc >0) {
310         rply->cnid = rqst->cnid;
311     }
312
313     LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u", 
314         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));
315
316     return rc;
317 }