]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbd_lookup.c
Remove bdb env on exit
[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 (for AFPVOL_CACHE volumes). In certain cases we can
14 use this hint to determince 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 <netatalk/endian.h>
120 #include <atalk/logger.h>
121 #include <atalk/cnid_dbd_private.h>
122
123 #include "pack.h"
124 #include "dbif.h"
125 #include "dbd.h"
126
127 /*
128  *  This returns the CNID corresponding to a particular file.  It will also fix
129  *  up the database if there's a problem.
130  */
131
132 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
133 {
134     unsigned char *buf;
135     DBT key, devdata, diddata;
136     int devino = 1, didname = 1; 
137     int rc;
138     cnid_t id_devino, id_didname;
139     u_int32_t type_devino  = (unsigned)-1;
140     u_int32_t type_didname = (unsigned)-1;
141     int update = 0;
142     
143     memset(&key, 0, sizeof(key));
144     memset(&diddata, 0, sizeof(diddata));
145     memset(&devdata, 0, sizeof(devdata));
146
147     rply->namelen = 0;
148     rply->cnid = 0;
149
150     LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START");
151     
152     buf = pack_cnid_data(rqst); 
153
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;
158
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;
163         return -1;
164     }
165     if (rc == 0) {
166         devino = 0;
167     }
168     else {
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);
172     }
173
174     key.data = buf + CNID_DID_OFS;
175     key.size = CNID_DID_LEN + rqst->namelen + 1;
176
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;
181         return -1;
182     }
183     if (rc == 0) {
184         didname = 0;
185     }
186     else {
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);
190     }
191
192     LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}", 
193         rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname);
194
195     /* Have we found anything at all ? */
196     if (!devino && !didname) {  
197         /* nothing found */
198         LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database", 
199             rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
200         rply->result = CNID_DBD_RES_NOTFOUND;
201         return 1;
202     }
203
204     /* Check for type (file/dir) mismatch */
205     if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) {
206
207         if (devino && (type_devino != rqst->type)) {
208             /* one is a dir one is a file, remove from db */
209
210             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino", 
211                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
212
213             if (! roflag) {
214                 rqst->cnid = id_devino;
215                 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
216                 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
217                 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
218                 if (rc < 0) {
219                     LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 
220                         rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
221                     return -1;
222                 }
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             if (! roflag) {
233                 rqst->cnid = id_didname;
234                 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
235                 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
236                 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
237                 if (rc < 0) {
238                     LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
239                         rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
240                     return -1;
241                 }
242             }
243         }
244
245         rply->result = CNID_DBD_RES_NOTFOUND;
246         return 1;
247     }
248
249     if (devino && didname && id_devino == id_didname) {
250         /* everything is fine */
251         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
252             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
253         rply->cnid = id_didname;
254         rply->result = CNID_DBD_RES_OK;
255         return 1;
256     }
257
258     if (devino && didname && id_devino != id_didname) {
259         /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
260         LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
261             ntohl(rqst->did), rqst->name, ntohl(id_didname),
262             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
263
264         if (! roflag) {
265             rqst->cnid = id_devino;
266             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
267                 return -1;
268
269             rqst->cnid = id_didname;
270             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
271                 return -1;
272         }
273         rply->result = CNID_DBD_RES_NOTFOUND;
274         return 1;
275     }
276
277     if ( ! didname) {
278         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",
279             ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
280         if (rqst->cnid == id_devino) {
281             LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
282             update = 1;
283         } else {
284             if ( ! roflag) {
285                 rqst->cnid = id_devino;
286                 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
287                     return -1;
288             }
289             rply->result = CNID_DBD_RES_NOTFOUND;
290             return 1;
291         }
292     }
293
294     if ( ! devino) {
295         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
296             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
297         if ( ! roflag) {
298             rqst->cnid = id_didname;
299             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
300                 return -1;
301         }
302         rply->result = CNID_DBD_RES_NOTFOUND;
303         return 1;
304     }
305
306     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
307     if (!update || roflag) {
308         rply->result = CNID_DBD_RES_NOTFOUND;
309         return 1;
310     }
311
312     /* Fix up the database */
313     rc = dbd_update(dbd, rqst, rply);
314     if (rc >0) {
315         rply->cnid = rqst->cnid;
316     }
317
318     LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u", 
319         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));
320
321     return rc;
322 }