]> arthur.barton.de Git - netatalk.git/blob - libatalk/cnid/cdb/cnid_cdb_add.c
big merge for db frontend and unicode.
[netatalk.git] / libatalk / cnid / cdb / cnid_cdb_add.c
1 /*
2  * $Id: cnid_cdb_add.c,v 1.1.4.1 2003-09-09 16:42:21 didg Exp $
3  *
4  * Copyright (c) 1999. Adrian Sun (asun@zoology.washington.edu)
5  * All Rights Reserved. See COPYRIGHT.
6  *
7  * cnid_add (db, dev, ino, did, name, hint):
8  * add a name to the CNID database. we use both dev/ino and did/name
9  * to keep track of things.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif /* HAVE_CONFIG_H */
15
16 #ifdef CNID_BACKEND_CDB
17
18 #include <stdio.h>
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <string.h>
22 #ifdef HAVE_UNISTD_H
23 #include <unistd.h>
24 #endif /* HAVE_UNISTD_H */
25 #ifdef HAVE_FCNTL_H
26 #include <fcntl.h>
27 #endif /* HAVE_FCNTL_H */
28 #include <errno.h>
29 #include <atalk/logger.h>
30 #ifdef HAVE_SYS_TIME_H
31 #include <sys/time.h>
32 #endif /* HAVE_SYS_TIME_H */
33
34 #include <db.h>
35 #include <netatalk/endian.h>
36
37 #include <atalk/adouble.h>
38 #include "cnid_cdb.h"
39 #include <atalk/util.h>
40
41 #include "cnid_cdb_private.h"
42
43     #define tid    NULL
44
45 #ifdef ATACC
46 char *make_cnid_data(const struct stat *st,const cnid_t did,
47                      const char *name, const int len)
48 {
49     static char start[CNID_HEADER_LEN + MAXPATHLEN + 1];
50     char *buf = start  +CNID_LEN;
51     u_int32_t i;
52
53     if (len > MAXPATHLEN)
54         return NULL;
55
56     memcpy(buf, &st->st_dev, sizeof(st->st_dev));
57     buf += sizeof(st->st_dev);
58
59     i = htonl(st->st_ino);
60     memcpy(buf , &st->st_ino, sizeof(st->st_ino));
61     buf += sizeof(st->st_ino);
62
63     i = S_ISDIR(st->st_mode)?1:0;
64     i = htonl(i);
65     memcpy(buf, &i, sizeof(i));
66     buf += sizeof(i);
67     
68     /* did is already in network byte order */
69     memcpy(buf, &did, sizeof(did));
70     buf += sizeof(did);
71
72     memcpy(buf, name, len);
73     *(buf + len) = '\0';
74
75     return start;
76 }    
77 #endif
78
79 extern int cnid_cdb_update(struct _cnid_db *cdb, const cnid_t id, const struct stat *st,
80                 const cnid_t did, const char *name, const int len);
81
82 /* ----------------------------- */
83 static cnid_t get_cnid(CNID_private *db)
84 {
85     DBT rootinfo_key, rootinfo_data;
86     DBC  *cursor;
87     int rc;
88     int flag;
89     cnid_t hint,id;
90     char buf[ROOTINFO_DATALEN];      
91      
92     if ((rc = db->db_cnid->cursor(db->db_cnid, NULL, &cursor, DB_WRITECURSOR) ) != 0) {
93         LOG(log_error, logtype_default, "get_cnid: Unable to get a cursor: %s", db_strerror(rc));
94         return CNID_INVALID;
95     }
96
97     memset(&rootinfo_key, 0, sizeof(rootinfo_key));
98     memset(&rootinfo_data, 0, sizeof(rootinfo_data));
99     rootinfo_key.data = ROOTINFO_KEY;
100     rootinfo_key.size = ROOTINFO_KEYLEN;
101
102     switch (rc = cursor->c_get(cursor, &rootinfo_key, &rootinfo_data, DB_SET)) {
103     case 0:
104         memcpy(&hint, (char *)rootinfo_data.data +CNID_TYPE_OFS, sizeof(hint));
105         id = ntohl(hint);
106         /* If we've hit the max CNID allowed, we return a fatal error.  CNID
107          * needs to be recycled before proceding. */
108         if (++id == CNID_INVALID) {
109             LOG(log_error, logtype_default, "cnid_add: FATAL: CNID database has reached its limit.");
110             errno = CNID_ERR_MAX;
111             goto cleanup;
112         }
113         hint = htonl(id);
114         flag = DB_CURRENT;
115         break;
116     case DB_NOTFOUND:
117         hint = htonl(CNID_START);
118         flag = DB_KEYFIRST;
119         break;
120     default:
121         LOG(log_error, logtype_default, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc));
122         errno = CNID_ERR_DB; 
123         goto cleanup;
124     }
125     
126     memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN);
127     rootinfo_data.data = buf;
128     rootinfo_data.size = ROOTINFO_DATALEN;
129     memcpy((char *)rootinfo_data.data +CNID_TYPE_OFS, &hint, sizeof(hint));
130     
131     switch (rc = cursor->c_put(cursor, &rootinfo_key, &rootinfo_data, flag)) {
132     case 0:
133         break;
134     default:
135         LOG(log_error, logtype_default, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc));
136         errno = CNID_ERR_DB; 
137         goto cleanup;
138     }
139     if ((rc = cursor->c_close(cursor)) != 0) {
140         LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc));
141         errno = CNID_ERR_DB; 
142         return CNID_INVALID;
143     }
144     return hint;
145 cleanup:
146     if ((rc = cursor->c_close(cursor)) != 0) {
147         LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc));
148         return CNID_INVALID;
149     }
150     return CNID_INVALID;
151 }
152
153 /* ------------------------ */
154 cnid_t cnid_cdb_add(struct _cnid_db *cdb, const struct stat *st,
155                 const cnid_t did, const char *name, const int len,
156                 cnid_t hint)
157 {
158     CNID_private *db;
159     DBT key, data;
160     cnid_t id;
161     int rc;
162
163     if (!cdb || !(db = cdb->_private) || !st || !name) {
164         errno = CNID_ERR_PARAM;
165         return CNID_INVALID;
166     }
167
168     /* Do a lookup. */
169     id = cnid_cdb_lookup(cdb, st, did, name, len);
170     /* ... Return id if it is valid, or if Rootinfo is read-only. */
171     if (id || (db->flags & CNIDFLAG_DB_RO)) {
172 #ifdef DEBUG
173         LOG(log_info, logtype_default, "cnid_add: Looked up did %u, name %s as %u", ntohl(did), name, ntohl(id));
174 #endif
175         return id;
176     }
177
178     /* Initialize our DBT data structures. */
179     memset(&key, 0, sizeof(key));
180     memset(&data, 0, sizeof(data));
181
182     if ((data.data = make_cnid_data(st, did, name, len)) == NULL) {
183         LOG(log_error, logtype_default, "cnid_add: Path name is too long");
184         errno = CNID_ERR_PATH;
185         return CNID_INVALID;
186     }
187     data.size = CNID_HEADER_LEN + len + 1;
188
189     if ((hint = get_cnid(db)) == 0) {
190          errno = CNID_ERR_DB;
191          return CNID_INVALID;
192     }
193     memcpy(data.data, &hint, sizeof(hint));
194     
195     key.data = &hint;
196     key.size = sizeof(hint);
197
198     /* Now we need to add the CNID data to the databases. */
199     if ((rc = db->db_cnid->put(db->db_cnid, tid, &key, &data, DB_NOOVERWRITE))) {
200         if (rc == EINVAL) {
201             /* if we have a duplicate
202              * on cnid it's a fatal error.
203              * on dev:inode
204              *   - leftover should have been delete before.
205              *   - a second process already updated the db
206              *   - it's a new file eg our file is already deleted and replaced
207              * on did:name leftover
208             */
209             if (cnid_cdb_update(cdb, hint, st, did, name, len)) {
210                 errno = CNID_ERR_DB;
211                 return CNID_INVALID;
212             }
213         }
214         else {
215             LOG(log_error, logtype_default
216                    , "cnid_add: Failed to add CNID for %s to database using hint %u: %s", 
217                    name, ntohl(hint), db_strerror(rc));  
218             errno = CNID_ERR_DB;
219             return CNID_INVALID;
220         }
221     }
222
223 #ifdef DEBUG
224     LOG(log_info, logtype_default, "cnid_add: Returned CNID for did %u, name %s as %u", ntohl(did), name, ntohl(hint));
225 #endif
226
227     return hint;
228 }
229
230 #endif /* CNID_BACKEND_CDB */