]> arthur.barton.de Git - netatalk.git/blob - etc/cnid_dbd/dbd_lookup.c
CNID salvation strategy
[netatalk.git] / etc / cnid_dbd / dbd_lookup.c
1 /*
2  * $Id: dbd_lookup.c,v 1.10 2009-11-24 12:01:04 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    ATTENTION:
11    whatever you change here, also change cmd_dbd_lookup.c !
12    cmd_dbd_lookup has an read-only mode but besides that it's the same.
13 */
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.
25
26
27 1) UNIX rename (mv)
28 -------------------
29 Name is changed but inode stays the same. We should try to keep the CNID.
30
31 15  2   f   1   1       file
32 -->
33 15  2   f   1   1       movedfile
34
35 Result in dbd_lookup (-: not found, +: found):
36 - devino
37 + didname
38
39 Possible solution:
40 Update.
41
42
43 2) UNIX copy (cp)
44 -----------------
45 Changed inode and name. Result is just a new file which will get a fresh CNID.
46 Unfortunately the old one gets orphaned.
47
48 15  2   f   1   1       file
49 -->
50 16  2   f   1   2       copyfile
51
52 Result in dbd_lookup:
53 - devino
54 - didname
55
56 Possible fixup solution:
57 Not possible. Only dbd -re can delete the orphaned CNID 15
58
59
60 3) Restore from backup
61 ----------------------
62 15  2   f   1   1       file
63 -->
64 15  2   f   1   2       file
65
66 Result in dbd_lookup:
67 - devino
68 + didname
69
70 Possible fixup solution:
71 Update.
72
73
74 4) inode reusage eg. UNIX emacs
75 -------------------------------
76 This one is tough:
77 emacs uses a backup file (file~). When saving because of inode reusage of the fs,
78 both files exchange inodes. There's probably no appropiate solution for all
79 scenarios where this might occur eg. for the emacs case it would be good to
80 preserve the CNIDs while probably in the general case we should assign new CNIDs
81 to both files:
82
83 General case:
84 15  2   f   1   1       file
85 -->
86 16  2   f   1   1       new_file_with_reused_inode
87
88 Emacs case:
89 15  2   f   1   1       file
90 16  2   f   1   2       file~
91 -->
92 ??  2   f   1   2       file
93 ??  2   f   1   1       file~
94
95 Result in dbd_lookup:
96 + devino
97 + didname
98
99 Possible fixup solution:
100 to be safe we must implement the general case, sorry emacs.
101 */
102
103 #ifdef HAVE_CONFIG_H
104 #include "config.h"
105 #endif /* HAVE_CONFIG_H */
106
107
108 #include <stdio.h>
109 #include <string.h>
110 #include <sys/param.h>
111 #include <errno.h>
112 #include <netatalk/endian.h>
113 #include <atalk/logger.h>
114 #include <atalk/cnid_dbd_private.h>
115
116 #include "pack.h"
117 #include "dbif.h"
118 #include "dbd.h"
119
120 /*
121  *  This returns the CNID corresponding to a particular file.  It will also fix
122  *  up the database if there's a problem.
123  */
124
125 int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
126 {
127     unsigned char *buf;
128     DBT key, devdata, diddata;
129     char dev[CNID_DEV_LEN];
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     
138     memset(&key, 0, sizeof(key));
139     memset(&diddata, 0, sizeof(diddata));
140     memset(&devdata, 0, sizeof(devdata));
141
142     rply->namelen = 0;
143     rply->cnid = 0;
144     
145     buf = pack_cnid_data(rqst); 
146     memcpy(dev, buf + CNID_DEV_OFS, CNID_DEV_LEN);
147
148     /* Look for a CNID.  We have two options: dev/ino or did/name.  If we
149        only get a match in one of them, that means a file has moved. */
150     key.data = buf + CNID_DEVINO_OFS;
151     key.size = CNID_DEVINO_LEN;
152
153     if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0))  < 0) {
154         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
155                 ntohl(rqst->did), rqst->name);
156         rply->result = CNID_DBD_RES_ERR_DB;
157         return -1;
158     }
159     if (rc == 0) {
160         devino = 0;
161     }
162     else {
163         memcpy(&id_devino, devdata.data, sizeof(rply->cnid));
164         memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino));
165         type_devino = ntohl(type_devino);
166     }
167
168     key.data = buf + CNID_DID_OFS;
169     key.size = CNID_DID_LEN + rqst->namelen + 1;
170
171     if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0))  < 0) {
172         LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s",
173                 ntohl(rqst->did), rqst->name);
174         rply->result = CNID_DBD_RES_ERR_DB;
175         return -1;
176     }
177     if (rc == 0) {
178         didname = 0;
179     }
180     else {
181         memcpy(&id_didname, diddata.data, sizeof(rply->cnid));
182         memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname));
183         type_didname = ntohl(type_didname);
184     }
185     
186     if (!devino && !didname) {  
187         /* not found */
188
189         LOG(log_debug, logtype_cnid, "cnid_lookup: dev/ino 0x%llx/0x%llx did %u name %s neither in devino nor didname", 
190             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name);
191
192         rply->result = CNID_DBD_RES_NOTFOUND;
193         return 1;
194     }
195
196     if (devino && didname && id_devino == id_didname && type_devino == rqst->type) {
197         /* the same */
198
199         LOG(log_debug, logtype_cnid, "cnid_lookup: Looked up dev/ino 0x%llx/0x%llx did %u name %s as %u", 
200             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name, ntohl(id_didname));
201
202         rply->cnid = id_didname;
203         rply->result = CNID_DBD_RES_OK;
204         return 1;
205     }
206     
207     /* 
208        Order matters for the next 2 ifs because both found a CNID but they do not match.
209        So in order to pick the CNID from "didname" it must come after devino.
210        See test cases laid out in dbd_lookup.c.
211     */
212     if (devino) {
213         LOG(log_maxdebug, logtype_cnid, "CNID resolve problem: server side rename oder reused inode for '%s'", rqst->name);
214         rqst->cnid = id_devino;
215         if (type_devino != rqst->type) {
216             /* same dev:inode but not same type one is a folder the other 
217              * is a file,it's an inode reused, delete the record
218             */
219             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) {
220                 return -1;
221             }
222         }
223         else {
224             update = 1;
225         }
226     }
227     if (didname) {
228         LOG(log_maxdebug, logtype_cnid, "CNID resolve problem: changed dev/ino for '%s'", rqst->name);
229         rqst->cnid = id_didname;
230         /* we have a did:name 
231          * if it's the same dev or not the same type
232          * just delete it
233         */
234         if (!memcmp(dev, (char *)diddata.data + CNID_DEV_OFS, CNID_DEV_LEN) ||
235                    type_didname != rqst->type) {
236             if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) {
237                 return -1;
238             }
239         }
240         else {
241             update = 1;
242         }
243     }
244     if (!update) {
245         rply->result = CNID_DBD_RES_NOTFOUND;
246         return 1;
247     }
248     /* Fix up the database. assume it was a file move and rename */
249     rc = dbd_update(dbd, rqst, rply);
250     if (rc >0) {
251         rply->cnid = rqst->cnid;
252     }
253
254     LOG(log_debug, logtype_cnid, "cnid_lookup: Looked up dev/ino 0x%llx/0x%llx did %u name %s as %u (needed update)", 
255         (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rqst->did), rqst->name, ntohl(rply->cnid));
256
257     return rc;
258 }