]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/tdb/cnid_tdb_add.c
3203eda94a9f80de30e180f44bd3744755d65b92
[netatalk.git] / libatalk / cnid / tdb / cnid_tdb_add.c
1 /*
2  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
3  * All Rights Reserved. See COPYRIGHT.
4  *
5  */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #ifdef CNID_BACKEND_TDB
12 #include "cnid_tdb.h"
13 #include <atalk/util.h>
14 #include <sys/param.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <atalk/logger.h>
19
20 static void make_devino_data(unsigned char *buf, dev_t dev, ino_t ino)
21 {
22     buf[CNID_DEV_LEN - 1] = dev; dev >>= 8;
23     buf[CNID_DEV_LEN - 2] = dev; dev >>= 8;
24     buf[CNID_DEV_LEN - 3] = dev; dev >>= 8;
25     buf[CNID_DEV_LEN - 4] = dev; dev >>= 8;
26     buf[CNID_DEV_LEN - 5] = dev; dev >>= 8;
27     buf[CNID_DEV_LEN - 6] = dev; dev >>= 8;
28     buf[CNID_DEV_LEN - 7] = dev; dev >>= 8;
29     buf[CNID_DEV_LEN - 8] = dev;
30
31     buf[CNID_DEV_LEN + CNID_INO_LEN - 1] = ino; ino >>= 8;
32     buf[CNID_DEV_LEN + CNID_INO_LEN - 2] = ino; ino >>= 8;
33     buf[CNID_DEV_LEN + CNID_INO_LEN - 3] = ino; ino >>= 8;
34     buf[CNID_DEV_LEN + CNID_INO_LEN - 4] = ino; ino >>= 8;
35     buf[CNID_DEV_LEN + CNID_INO_LEN - 5] = ino; ino >>= 8;
36     buf[CNID_DEV_LEN + CNID_INO_LEN - 6] = ino; ino >>= 8;
37     buf[CNID_DEV_LEN + CNID_INO_LEN - 7] = ino; ino >>= 8;
38     buf[CNID_DEV_LEN + CNID_INO_LEN - 8] = ino;    
39 }
40
41 unsigned char *make_tdb_data(uint32_t flags, const struct stat *st,const cnid_t did,
42                      const char *name, const size_t len)
43 {
44     static unsigned char start[CNID_HEADER_LEN + MAXPATHLEN + 1];
45     unsigned char *buf = start  +CNID_LEN;
46     uint32_t i;
47
48     if (len > MAXPATHLEN)
49         return NULL;
50
51     make_devino_data(buf, !(flags & CNID_FLAG_NODEV)?st->st_dev:0, st->st_ino);
52     buf += CNID_DEVINO_LEN;
53
54     i = S_ISDIR(st->st_mode)?1:0;
55     i = htonl(i);
56     memcpy(buf, &i, sizeof(i));
57     buf += sizeof(i);
58     
59     /* did is already in network byte order */
60     memcpy(buf, &did, sizeof(did));
61     buf += sizeof(did);
62
63     memcpy(buf, name, len);
64     *(buf + len) = '\0';
65
66     return start;
67 }    
68
69 /* add an entry to the CNID databases. we do this as a transaction
70  * to prevent messiness. 
71  * key:   cnid
72  * data:
73  */
74 static int add_cnid (struct _cnid_tdb_private *db, TDB_DATA *key, TDB_DATA *data) {
75     TDB_DATA altkey, altdata;
76
77     memset(&altkey, 0, sizeof(altkey));
78     memset(&altdata, 0, sizeof(altdata));
79
80
81     /* main database */
82     if (tdb_store(db->tdb_cnid, *key, *data, TDB_REPLACE)) {
83         goto abort;
84     }
85
86     /* dev/ino database */
87     altkey.dptr = data->dptr +CNID_DEVINO_OFS;
88     altkey.dsize = CNID_DEVINO_LEN;
89     altdata.dptr = key->dptr;
90     altdata.dsize = key->dsize;
91     if (tdb_store(db->tdb_devino, altkey, altdata, TDB_REPLACE)) {
92         goto abort;
93     }
94
95     /* did/name database */
96     altkey.dptr = data->dptr +CNID_DID_OFS;
97     altkey.dsize = data->dsize -CNID_DID_OFS;
98     if (tdb_store(db->tdb_didname, altkey, altdata, TDB_REPLACE)) {
99         goto abort;
100     }
101     return 0;
102
103 abort:
104     return -1;
105 }
106
107 /* ----------------------- */
108 static cnid_t get_cnid(struct _cnid_tdb_private *db)
109 {
110     TDB_DATA rootinfo_key, data;
111     cnid_t hint,id;
112     
113     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
114     memset(&data, 0, sizeof(data));
115     rootinfo_key.dptr = (unsigned char *)ROOTINFO_KEY;
116     rootinfo_key.dsize = ROOTINFO_KEYLEN;
117     
118     tdb_chainlock(db->tdb_didname, rootinfo_key);  
119     data = tdb_fetch(db->tdb_didname, rootinfo_key);
120     if (data.dptr)
121     {
122         memcpy(&hint, data.dptr, sizeof(cnid_t));
123         free(data.dptr);
124         id = ntohl(hint);
125         /* If we've hit the max CNID allowed, we return a fatal error.  CNID
126          * needs to be recycled before proceding. */
127         if (++id == CNID_INVALID) {
128             LOG(log_error, logtype_default, "cnid_add: FATAL: CNID database has reached its limit.");
129             errno = CNID_ERR_MAX;
130             goto cleanup;
131         }
132         hint = htonl(id);
133     }
134     else {
135         hint = htonl(CNID_START);
136     }
137     
138     memset(&data, 0, sizeof(data));
139     data.dptr = (unsigned char *)&hint;
140     data.dsize = sizeof(hint);
141     if (tdb_store(db->tdb_didname, rootinfo_key, data, TDB_REPLACE)) {
142         goto cleanup;
143     }
144
145     tdb_chainunlock(db->tdb_didname, rootinfo_key );  
146     return hint;
147 cleanup:
148     tdb_chainunlock(db->tdb_didname, rootinfo_key);  
149     return CNID_INVALID;
150 }
151
152
153 /* ------------------------ */
154 cnid_t cnid_tdb_add(struct _cnid_db *cdb, const struct stat *st,
155                     cnid_t did, const char *name, size_t len, cnid_t hint)
156 {
157     const struct stat *lstp;
158     cnid_t id;
159     struct _cnid_tdb_private *priv;
160     TDB_DATA key, data; 
161     int rc;      
162     
163     if (!cdb || !(priv = cdb->_private) || !st || !name) {
164         errno = CNID_ERR_PARAM;
165         return CNID_INVALID;
166     }
167     /* Do a lookup. */
168     id = cnid_tdb_lookup(cdb, st, did, name, len);
169     /* ... Return id if it is valid, or if Rootinfo is read-only. */
170     if (id || (priv->flags & CNIDFLAG_DB_RO)) {
171         return id;
172     }
173
174 #if 0
175     struct stat lst;
176     lstp = lstat(name, &lst) < 0 ? st : &lst;
177 #endif
178     lstp = st;
179
180     /* Initialize our DBT data structures. */
181     memset(&key, 0, sizeof(key));
182     memset(&data, 0, sizeof(data));
183
184     key.dptr = (unsigned char *)&hint;
185     key.dsize = sizeof(cnid_t);
186     if ((data.dptr = make_tdb_data(cdb->flags, lstp, did, name, len)) == NULL) {
187         LOG(log_error, logtype_default, "tdb_add: Path name is too long");
188         errno = CNID_ERR_PATH;
189         return CNID_INVALID;
190     }
191     data.dsize = CNID_HEADER_LEN + len + 1;
192     hint = get_cnid(priv);
193     if (hint == 0) {
194         errno = CNID_ERR_DB;
195         return CNID_INVALID;
196     }
197     memcpy(data.dptr, &hint, sizeof(hint));
198     
199     /* Now we need to add the CNID data to the databases. */
200     rc = add_cnid(priv, &key, &data);
201     if (rc) {
202         LOG(log_error, logtype_default, "tdb_add: Failed to add CNID for %s to database using hint %u", name, ntohl(hint));
203         errno = CNID_ERR_DB;
204         return CNID_INVALID;
205     }
206
207     return hint;
208 }
209
210 #endif