]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbd_lookup.c
don't trash the db if a file is moved to a different directory without updating the db
[netatalk.git] / etc / cnid_dbd / dbd_lookup.c
1 /*
2  * $Id: dbd_lookup.c,v 1.13 2009-11-28 15:42:05 didg 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.
65
66 3) Restore from backup ie change of inode number -- or emacs
67 ------------------------------------------------------------
68
69 15  2   f   1   1       file
70 -->
71 15  2   f   1   2       file
72
73 Result in dbd_lookup:
74 - devino
75 + didname
76
77 Possible fixup solution:
78 test-suite test235 tests and ensures that the CNID is _changed_. The reason for
79 this is somewhat lost in time, but nevertheless we believe our test suite.
80
81 Similar things happen with emas: emacs uses a backup file (file~). When saving
82 because of inode reusage of the fs, both files most likely exchange inodes.
83
84 15  2   f   1   1       file
85 16  2   f   1   2       file~
86 --> this would be nice:
87 15  2   f   1   2       file
88 16  2   f   1   1       file~
89 --> but for the reasons described above we must implement
90 17  2   f   1   2       file
91 18  2   f   1   1       file~
92
93 Result in dbd_lookup for the emacs case:
94 + devino --> CNID: 16
95 + didname -> CNID: 15
96 devino search and didname search result in different CNIDs !!
97
98 Possible fixup solution:
99 to be safe we must assign new CNIDs to both files.
100 */
101
102 #ifdef HAVE_CONFIG_H
103 #include "config.h"
104 #endif /* HAVE_CONFIG_H */
105
106
107 #include <stdio.h>
108 #include <string.h>
109 #include <sys/param.h>
110 #include <errno.h>
111 #include <netatalk/endian.h>
112 #include <atalk/logger.h>
113 #include <atalk/cnid_dbd_private.h>
114
115 #include "pack.h"
116 #include "dbif.h"
117 #include "dbd.h"
118
119 /*
120  *  This returns the CNID corresponding to a particular file.  It will also fix
121  *  up the database if there's a problem.
122  */
123
124 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
125 {
126     unsigned char *buf;
127     DBT key, devdata, diddata;
128     int devino = 1, didname = 1; 
129     int rc;
130     cnid_t id_devino, id_didname;
131     u_int32_t type_devino  = (unsigned)-1;
132     u_int32_t type_didname = (unsigned)-1;
133     int update = 0;
134     
135     memset(&key, 0, sizeof(key));
136     memset(&diddata, 0, sizeof(diddata));
137     memset(&devdata, 0, sizeof(devdata));
138
139     rply->namelen = 0;
140     rply->cnid = 0;
141     
142     buf = pack_cnid_data(rqst); 
143
144     /* Look for a CNID.  We have two options: dev/ino or did/name.  If we
145        only get a match in one of them, that means a file has moved. */
146     key.data = buf + CNID_DEVINO_OFS;
147     key.size = CNID_DEVINO_LEN;
148
149     if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0))  < 0) {
150         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
151             ntohl(rqst->did), rqst->name);
152         rply->result = CNID_DBD_RES_ERR_DB;
153         return -1;
154     }
155     if (rc == 0) {
156         devino = 0;
157     }
158     else {
159         memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
160         memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
161         type_devino = ntohl(type_devino);
162     }
163
164     key.data = buf + CNID_DID_OFS;
165     key.size = CNID_DID_LEN + rqst->namelen + 1;
166
167     if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0))  < 0) {
168         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
169             ntohl(rqst->did), rqst->name);
170         rply->result = CNID_DBD_RES_ERR_DB;
171         return -1;
172     }
173     if (rc == 0) {
174         didname = 0;
175     }
176     else {
177         memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
178         memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
179         type_didname = ntohl(type_didname);
180     }
181
182     /* Have we found anything at all ? */
183     if (!devino && !didname) {  
184         /* nothing found */
185         LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database", 
186             rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
187         rply->result = CNID_DBD_RES_NOTFOUND;
188         return 1;
189     }
190
191     /* Check for type (file/dir) mismatch */
192     if (devino && (type_devino != rqst->type)) {
193         /* one is a dir one is a file, remove from db */
194         if (! roflag) {
195             rqst->cnid = id_devino;
196             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
197                 return -1;
198         }
199         rply->result = CNID_DBD_RES_NOTFOUND;
200         return 1;
201     } else if (didname && (type_didname != rqst->type)) {
202         /* same: one is a dir one is a file, remove from db */
203         if (! roflag) {
204             rqst->cnid = id_didname;
205             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
206                 return -1;
207         }
208         rply->result = CNID_DBD_RES_NOTFOUND;
209         return 1;
210     }
211
212     if (devino && didname && id_devino == id_didname) {
213         /* everything is fine */
214         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
215             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
216         rply->cnid = id_didname;
217         rply->result = CNID_DBD_RES_OK;
218         return 1;
219     }
220
221     if (devino && didname && id_devino != id_didname) {
222         /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
223         LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
224             ntohl(rqst->did), rqst->name, ntohl(id_didname),
225             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
226
227         if (! roflag) {
228             rqst->cnid = id_devino;
229             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
230                 return -1;
231
232             rqst->cnid = id_didname;
233             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
234                 return -1;
235         }
236         rply->result = CNID_DBD_RES_NOTFOUND;
237         return 1;
238     }
239
240     if ( ! didname) {
241         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode",
242             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
243         /* Case 2) ? */
244         if (strcmp(rqst->name, (char *)devdata.data + CNID_NAME_OFS) == 0) {
245             rqst->cnid = id_devino;
246             LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv from one dir to another");
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 }