]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbd_lookup.c
Refine CNID salvatio strategies
[netatalk.git] / etc / cnid_dbd / dbd_lookup.c
1 /*
2  * $Id: dbd_lookup.c,v 1.11 2009-11-25 14:59:15 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.
65
66 3) Restore from backup
67 ----------------------
68 15  2   f   1   1       file
69 -->
70 15  2   f   1   2       file
71
72 Result in dbd_lookup:
73 - devino
74 + didname
75
76 Possible fixup solution:
77 Update.
78
79 4) emacs
80 --------
81 emacs uses a backup file (file~). When saving because of inode reusage of the fs,
82 both files exchange inodes.
83
84 General case for inode reusage:
85 15  2   f   1   1       file
86 -->
87 16  2   f   1   1       new_file_with_reused_inode
88
89 Result in dbd_lookup:
90 + devino
91 - didname
92
93 Emacs case:
94 15  2   f   1   1       file
95 16  2   f   1   2       file~
96 --> this would be nice:
97 15  2   f   1   2       file
98 16  2   f   1   1       file~
99 --> but because we must follow the general case you get this:
100 17  2   f   1   2       file
101 18  2   f   1   1       file~
102
103 Result in dbd_lookup for the emacs case:
104 + devino --> CNID: 16
105 + didname -> CNID: 15
106 devino search and didname search result in different CNIDs !!
107
108 Possible fixup solution:
109 to be safe we must implement the general case, sorry emacs.
110 */
111
112 #ifdef HAVE_CONFIG_H
113 #include "config.h"
114 #endif /* HAVE_CONFIG_H */
115
116
117 #include <stdio.h>
118 #include <string.h>
119 #include <sys/param.h>
120 #include <errno.h>
121 #include <netatalk/endian.h>
122 #include <atalk/logger.h>
123 #include <atalk/cnid_dbd_private.h>
124
125 #include "pack.h"
126 #include "dbif.h"
127 #include "dbd.h"
128
129 /*
130  *  This returns the CNID corresponding to a particular file.  It will also fix
131  *  up the database if there's a problem.
132  */
133
134 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
135 {
136     unsigned char *buf;
137     DBT key, devdata, diddata;
138     int devino = 1, didname = 1; 
139     int rc;
140     cnid_t id_devino, id_didname;
141     u_int32_t type_devino  = (unsigned)-1;
142     u_int32_t type_didname = (unsigned)-1;
143     int update = 0;
144     
145     memset(&key, 0, sizeof(key));
146     memset(&diddata, 0, sizeof(diddata));
147     memset(&devdata, 0, sizeof(devdata));
148
149     rply->namelen = 0;
150     rply->cnid = 0;
151     
152     buf = pack_cnid_data(rqst); 
153
154     /* Look for a CNID.  We have two options: dev/ino or did/name.  If we
155        only get a match in one of them, that means a file has moved. */
156     key.data = buf + CNID_DEVINO_OFS;
157     key.size = CNID_DEVINO_LEN;
158
159     if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0))  < 0) {
160         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
161             ntohl(rqst->did), rqst->name);
162         rply->result = CNID_DBD_RES_ERR_DB;
163         return -1;
164     }
165     if (rc == 0) {
166         devino = 0;
167     }
168     else {
169         memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
170         memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
171         type_devino = ntohl(type_devino);
172     }
173
174     key.data = buf + CNID_DID_OFS;
175     key.size = CNID_DID_LEN + rqst->namelen + 1;
176
177     if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0))  < 0) {
178         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
179             ntohl(rqst->did), rqst->name);
180         rply->result = CNID_DBD_RES_ERR_DB;
181         return -1;
182     }
183     if (rc == 0) {
184         didname = 0;
185     }
186     else {
187         memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
188         memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
189         type_didname = ntohl(type_didname);
190     }
191
192     /* Have we found anything at all ? */
193     if (!devino && !didname) {  
194         /* nothing found */
195         LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database", 
196             rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
197         rply->result = CNID_DBD_RES_NOTFOUND;
198         return 1;
199     }
200
201     /* Check for type (file/dir) mismatch */
202     if (devino && (type_devino != rqst->type)) {
203         /* one is a dir one is a file, remove from db */
204         rqst->cnid = id_devino;
205         if (! roflag)
206             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
207                 return -1;
208         rply->result = CNID_DBD_RES_NOTFOUND;
209         return 1;
210     } else if (didname && (type_didname != rqst->type)) {
211         /* same: one is a dir one is a file, remove from db */
212         rqst->cnid = id_didname;
213         if (! roflag)
214             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
215                 return -1;
216         rply->result = CNID_DBD_RES_NOTFOUND;
217         return 1;
218     }
219
220     if (devino && didname && id_devino == id_didname) {
221         /* everything is fine */
222         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u",
223             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname));
224         rply->cnid = id_didname;
225         rply->result = CNID_DBD_RES_OK;
226         return 1;
227     }
228
229     if (devino && didname && id_devino != id_didname) {
230         /* CNIDs don't match, something of a worst case! */
231         LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
232             ntohl(rqst->did), rqst->name, ntohl(id_didname),
233             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
234
235         /* Something like 5), the emacs case (see above), remove it all */
236         if (! roflag) {
237             rqst->cnid = id_devino;
238             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
239                 return -1;
240
241             rqst->cnid = id_didname;
242             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
243                 return -1;
244         }
245         rply->result = CNID_DBD_RES_NOTFOUND;
246         return 1;
247     }
248
249     if ( ! didname) {
250         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode",
251             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
252         rqst->cnid = id_devino;
253         /* Case 2) ? */
254         if (strcmp(rqst->name, (char *)devdata.data + CNID_NAME_OFS) == 0) {
255             LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv from one dir to another");
256             update = 1;
257         } else {
258             if ( ! roflag)
259                 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
260                     return -1;
261             rply->result = CNID_DBD_RES_NOTFOUND;
262             return 1;
263         }
264     }
265
266     if ( ! devino) {
267         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
268             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
269         rqst->cnid = id_didname;
270         update = 1;
271     }
272
273     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
274     if (!update || roflag) {
275         rply->result = CNID_DBD_RES_NOTFOUND;
276         return 1;
277     }
278
279     /* Fix up the database */
280     rc = dbd_update(dbd, rqst, rply);
281     if (rc >0) {
282         rply->cnid = rqst->cnid;
283     }
284
285     LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u", 
286         ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid));
287
288     return rc;
289 }