]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbd_lookup.c
f77b9fc2cb5f95a3abea20009fee1d980a7296ed
[netatalk.git] / etc / cnid_dbd / dbd_lookup.c
1 /*
2  *
3  * Copyright (C) Joerg Lenneis 2003
4  * Copyright (C) Frank Lahm 2009
5  * All Rights Reserved.  See COPYING.
6  */
7
8 /* 
9 CNID salvation spec:
10 general rule: better safe then sorry, so we always delete CNIDs and assign
11 new ones in case of a lookup mismatch. afpd also sends us the CNID found
12 in the adouble file. In certain cases we can use this hint to determince
13 the right CNID.
14
15
16 The lines...
17
18 Id  Did T   Dev Inode   Name
19 ============================
20 a   b   c   d   e       name1
21 -->
22 f   g   h   i   h       name2
23
24 ...are the expected results of certain operations. (f) is the speced CNID, in some
25 cases it's only intermediate as described in the text and is overridden by another
26 spec.
27
28 1) UNIX rename (via mv) or inode reusage(!)
29 -------------------------------------------
30 Name is possibly changed (rename case) but inode is the same.
31 We should try to keep the CNID, but we cant, because inode reusage is probably
32 much to frequent.
33
34 rename:
35 15  2   f   1   1       file
36 -->
37 15  x   f   1   1       renamedfile
38
39 inode reusage:
40 15  2   f   1   1       file
41 -->
42 16  y   f   1   1       inodereusagefile
43
44 Result in dbd_lookup (-: not found, +: found):
45 + devino
46 - didname
47
48 Possible solution:
49 None. Delete old data, file gets new CNID in both cases (rename and inode).
50 If we got a hint and hint matches the CNID from devino we keep it and update
51 the record.
52
53 2) UNIX mv from one folder to another
54 ----------------------------------------
55 Name is unchanged and inode stays the same, but DID is different.
56 We should try to keep the CNID.
57
58 15  2   f   1   1       file
59 -->
60 15  x   f   1   1       file
61
62 Result in dbd_lookup:
63 + devino
64 - didname
65
66 Possible solution:
67 strcmp names, if they match keep CNID. Unfortunately this also can't be
68 distinguished from a new file with a reused inode. So me must assign
69 a new CNID.
70 If we got a hint and hint matches the CNID from devino we keep it and update
71 the record.
72
73 3) Restore from backup ie change of inode number -- or emacs
74 ------------------------------------------------------------
75
76 15  2   f   1   1       file
77 -->
78 15  2   f   1   2       file
79
80 Result in dbd_lookup:
81 - devino
82 + didname
83
84 Possible fixup solution:
85 test-suite test235 tests and ensures that the CNID is _changed_. The reason for
86 this is somewhat lost in time, but nevertheless we believe our test suite.
87
88 Similar things happen with emas: emacs uses a backup file (file~). When saving
89 because of inode reusage of the fs, both files most likely exchange inodes.
90
91 15  2   f   1   1       file
92 16  2   f   1   2       file~
93 --> this would be nice:
94 15  2   f   1   2       file
95 16  2   f   1   1       file~
96 --> but for the reasons described above we must implement
97 17  2   f   1   2       file
98 18  2   f   1   1       file~
99
100 Result in dbd_lookup for the emacs case:
101 + devino --> CNID: 16
102 + didname -> CNID: 15
103 devino search and didname search result in different CNIDs !!
104
105 Possible fixup solution:
106 to be safe we must assign new CNIDs to both files.
107 */
108
109 #ifdef HAVE_CONFIG_H
110 #include "config.h"
111 #endif /* HAVE_CONFIG_H */
112
113
114 #include <stdio.h>
115 #include <string.h>
116 #include <sys/param.h>
117 #include <errno.h>
118 #include <arpa/inet.h>
119
120 #include <atalk/logger.h>
121 #include <atalk/cnid_dbd_private.h>
122 #include <atalk/cnid.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             rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
285             return 1;
286         }
287     }
288
289     if ( ! devino) {
290         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
291             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
292         rqst->cnid = id_didname;
293         if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
294             return -1;
295         rply->result = CNID_DBD_RES_NOTFOUND;
296         rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
297         return 1;
298     }
299
300     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
301     if (!update) {
302         rply->result = CNID_DBD_RES_NOTFOUND;
303         return 1;
304     }
305
306     /* Fix up the database */
307     rc = dbd_update(dbd, rqst, rply);
308     if (rc >0) {
309         rply->cnid = rqst->cnid;
310     }
311
312     LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u", 
313         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));
314
315     return rc;
316 }