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