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