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