]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbd_lookup.c
0ab85f9944904d2fbadab36155042398b55a97cc
[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
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)
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             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         if (didname && (type_didname != rqst->type)) {
226             /* same: one is a dir one is a file, remove from db */
227
228             LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname", 
229                 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
230
231             rqst->cnid = id_didname;
232             rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
233             rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
234             rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
235             if (rc < 0) {
236                 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
237                     rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
238                 return -1;
239             }
240         }
241
242         rply->result = CNID_DBD_RES_NOTFOUND;
243         return 1;
244     }
245
246     if (devino && didname && id_devino == id_didname) {
247         /* everything is fine */
248         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
249             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
250         rply->cnid = id_didname;
251         rply->result = CNID_DBD_RES_OK;
252         return 1;
253     }
254
255     if (devino && didname && id_devino != id_didname) {
256         /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
257         LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
258             ntohl(rqst->did), rqst->name, ntohl(id_didname),
259             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
260
261         rqst->cnid = id_devino;
262         if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
263             return -1;
264
265         rqst->cnid = id_didname;
266         if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
267             return -1;
268
269         rply->result = CNID_DBD_RES_NOTFOUND;
270         return 1;
271     }
272
273     if ( ! didname) {
274         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",
275             ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
276         if (rqst->cnid == id_devino) {
277             LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
278             update = 1;
279         } else {
280             rqst->cnid = id_devino;
281             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
282                 return -1;
283             rply->result = CNID_DBD_RES_NOTFOUND;
284             return 1;
285         }
286     }
287
288     if ( ! devino) {
289         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
290             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
291         rqst->cnid = id_didname;
292         if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
293             return -1;
294         rply->result = CNID_DBD_RES_NOTFOUND;
295         return 1;
296     }
297
298     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
299     if (!update) {
300         rply->result = CNID_DBD_RES_NOTFOUND;
301         return 1;
302     }
303
304     /* Fix up the database */
305     rc = dbd_update(dbd, rqst, rply);
306     if (rc >0) {
307         rply->cnid = rqst->cnid;
308     }
309
310     LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u", 
311         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));
312
313     return rc;
314 }