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