]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cnid_lookup.c
Fix a bunch of problems with database contention and corruption. Note:
[netatalk.git] / libatalk / cnid / cnid_lookup.c
1 /*
2  * $Id: cnid_lookup.c,v 1.7 2001-09-21 15:08:37 jmarcus Exp $
3  */
4
5 #ifdef HAVE_CONFIG_H
6 #include "config.h"
7 #endif /* HAVE_CONFIG_H */
8
9 #ifdef CNID_DB
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/param.h>
13 #include <sys/stat.h>
14 #include <syslog.h>
15 #include <errno.h>
16
17 #include <db.h>
18 #include <netatalk/endian.h>
19 #include <atalk/adouble.h>
20 #include <atalk/cnid.h>
21
22 #include "cnid_private.h"
23
24 #define LOGFILEMAX    100  /* kbytes */
25 #define CHECKTIMEMAX   30  /* minutes */
26
27 /* this returns the cnid corresponding to a particular file. it will
28    also fix up the various databases if there's a problem. */
29 cnid_t cnid_lookup(void *CNID,
30                    const struct stat *st, const cnid_t did, 
31                    const char *name, const int len)
32 {
33   char *buf;
34   CNID_private *db;
35   DBT key, devdata, diddata;
36   int devino = 1, didname = 1;
37   cnid_t id = 0;
38   int rc = 0;
39
40   int debug = 0;
41
42   if (!(db = CNID) || !st || !name)
43     return 0;
44
45   /* do a little checkpointing if necessary. i stuck it here as
46    * cnid_lookup gets called when we do directory lookups. only do
47    * this if we're using a read-write database. */
48   if ((db->flags & CNIDFLAG_DB_RO) == 0) {
49     rc = txn_checkpoint(db->dbenv, LOGFILEMAX, CHECKTIMEMAX, 0);
50     while (rc == DB_INCOMPLETE)
51       rc = txn_checkpoint(db->dbenv, 0, 0, 0);
52         if (rc) {
53           syslog(LOG_ERR, "cnid_lookup: txn_checkpoint failed with: %d", rc);
54           return 0;
55         }
56   }
57
58  if ((buf = make_cnid_data(st, did, name, len)) == NULL) {
59     syslog(LOG_ERR, "cnid_lookup: path name too long");
60     return 0;
61   }
62
63   memset(&key, 0, sizeof(key));
64   memset(&devdata, 0, sizeof(devdata));
65
66   /* look for a CNID. we have two options: dev/ino or did/name. if we
67      only get a match on one of them, that means a file has moved. */
68   key.data = buf; /* dev/ino is the first part of the buffer */
69   key.size = CNID_DEVINO_LEN;
70   while ((rc = db->db_devino->get(db->db_devino, NULL,
71                                     &key, &devdata, 0))) {
72     if (rc == DB_LOCK_DEADLOCK)
73       continue;
74
75     if (rc == DB_NOTFOUND) {
76       devino = 0;
77       break;
78     }
79
80     syslog(LOG_ERR, "cnid_lookup: can't get CNID(%u/%u) (%d)",
81            st->st_dev, st->st_ino, rc);
82     return 0;
83   }
84
85   /* did/name is right afterwards. */
86   key.data = buf + CNID_DEVINO_LEN;
87   key.size = CNID_DID_LEN + len + 1;
88   memset(&diddata, 0, sizeof(diddata));
89   while ((rc = db->db_didname->get(db->db_didname, NULL,
90                                        &key, &diddata, 0))) {
91     if (rc == DB_LOCK_DEADLOCK)
92       continue;
93
94     if (rc == DB_NOTFOUND) {
95       didname = 0;
96       break;
97     }
98
99     syslog(LOG_ERR, "cnid_lookup: can't get CNID(%u:%s) (%d)",
100            did, name, rc);
101     return 0;
102   }
103
104   /* set id. honor did/name over dev/ino as dev/ino isn't necessarily
105    * 1-1. */
106   if (didname) {
107     memcpy(&id, diddata.data, sizeof(id));
108   } else if (devino) {
109     memcpy(&id, devdata.data, sizeof(id));
110   }
111
112   /* either entries in both databases exist or neither of them do. */
113   if ((devino && didname) || !(devino || didname)) {
114     if (debug)
115       syslog(LOG_ERR, "cnid_lookup: looked up did %d, name %s as %d", did, name, id);
116     return id;
117   }
118   /* fix up the databases */
119   cnid_update(db, id, st, did, name, len);
120   if (debug)
121     syslog(LOG_ERR, "cnid_lookup: looked up did %d, name %s as %d (needed update)", did, name, id);
122   return id;
123 }
124 #endif /* CNID_DB */