1/* 2 * $Id: cnid_cdb_add.c,v 1.8 2009-11-20 17:22:11 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#include "cnid_cdb_private.h" 18 19extern int cnid_cdb_update(struct _cnid_db *cdb, const cnid_t id, const struct stat *st, 20 const cnid_t did, char *name, const size_t len); 21 22 23#define tid NULL 24 25static void make_devino_data(unsigned char *buf, dev_t dev, ino_t ino) 26{ 27 buf[CNID_DEV_LEN - 1] = dev; dev >>= 8; 28 buf[CNID_DEV_LEN - 2] = dev; dev >>= 8; 29 buf[CNID_DEV_LEN - 3] = dev; dev >>= 8; 30 buf[CNID_DEV_LEN - 4] = dev; dev >>= 8; 31 buf[CNID_DEV_LEN - 5] = dev; dev >>= 8; 32 buf[CNID_DEV_LEN - 6] = dev; dev >>= 8; 33 buf[CNID_DEV_LEN - 7] = dev; dev >>= 8; 34 buf[CNID_DEV_LEN - 8] = dev; 35 36 buf[CNID_DEV_LEN + CNID_INO_LEN - 1] = ino; ino >>= 8; 37 buf[CNID_DEV_LEN + CNID_INO_LEN - 2] = ino; ino >>= 8; 38 buf[CNID_DEV_LEN + CNID_INO_LEN - 3] = ino; ino >>= 8; 39 buf[CNID_DEV_LEN + CNID_INO_LEN - 4] = ino; ino >>= 8; 40 buf[CNID_DEV_LEN + CNID_INO_LEN - 5] = ino; ino >>= 8; 41 buf[CNID_DEV_LEN + CNID_INO_LEN - 6] = ino; ino >>= 8; 42 buf[CNID_DEV_LEN + CNID_INO_LEN - 7] = ino; ino >>= 8; 43 buf[CNID_DEV_LEN + CNID_INO_LEN - 8] = ino; 44} 45 46unsigned char *make_cnid_data(u_int32_t flags, const struct stat *st, const cnid_t did, 47 const char *name, const size_t len) 48{ 49 static unsigned char start[CNID_HEADER_LEN + MAXPATHLEN + 1]; 50 unsigned char *buf = start +CNID_LEN; 51 u_int32_t i; 52 53 if (len > MAXPATHLEN) 54 return NULL; 55 56 make_devino_data(buf, !(flags & CNID_FLAG_NODEV)?st->st_dev:0, st->st_ino); 57 buf += CNID_DEVINO_LEN; 58 59 i = S_ISDIR(st->st_mode)?1:0; 60 i = htonl(i); 61 memcpy(buf, &i, sizeof(i)); 62 buf += sizeof(i); 63 64 /* did is already in network byte order */ 65 memcpy(buf, &did, sizeof(did)); 66 buf += sizeof(did); 67 68 memcpy(buf, name, len); 69 *(buf + len) = '\0'; 70 71 return start; 72} 73 74/* --------------- */ 75static int db_stamp(void *buffer, size_t size) 76{ 77time_t t; 78 memset(buffer, 0, size); 79 /* return the current time. */ 80 if (size < sizeof(time_t)) 81 return -1; 82 t = time(NULL); 83 memcpy(buffer,&t, sizeof(time_t)); 84 return 0; 85 86} 87 88 89/* ----------------------------- */ 90static cnid_t get_cnid(CNID_private *db) 91{ 92 DBT rootinfo_key, rootinfo_data; 93 DBC *cursor; 94 int rc; 95 int flag, setstamp=0; 96 cnid_t hint,id; 97 char buf[ROOTINFO_DATALEN]; 98 char stamp[CNID_DEV_LEN]; 99 100 if ((rc = db->db_cnid->cursor(db->db_cnid, NULL, &cursor, DB_WRITECURSOR) ) != 0) { 101 LOG(log_error, logtype_default, "get_cnid: Unable to get a cursor: %s", db_strerror(rc)); 102 return CNID_INVALID; 103 } 104 105 memset(&rootinfo_key, 0, sizeof(rootinfo_key)); 106 memset(&rootinfo_data, 0, sizeof(rootinfo_data)); 107 rootinfo_key.data = ROOTINFO_KEY; 108 rootinfo_key.size = ROOTINFO_KEYLEN; 109 110 switch (rc = cursor->c_get(cursor, &rootinfo_key, &rootinfo_data, DB_SET)) { 111 case 0: 112 memcpy(&hint, (char *)rootinfo_data.data +CNID_TYPE_OFS, sizeof(hint)); 113 id = ntohl(hint); 114 /* If we've hit the max CNID allowed, we return a fatal error. CNID 115 * needs to be recycled before proceding. */ 116 if (++id == CNID_INVALID) { 117 LOG(log_error, logtype_default, "cnid_add: FATAL: CNID database has reached its limit."); 118 errno = CNID_ERR_MAX; 119 goto cleanup; 120 } 121 hint = htonl(id); 122 flag = DB_CURRENT; 123 break; 124 case DB_NOTFOUND: 125 hint = htonl(CNID_START); 126 flag = DB_KEYFIRST; 127 setstamp = 1; 128 break; 129 default: 130 LOG(log_error, logtype_default, "cnid_add: Unable to lookup rootinfo: %s", db_strerror(rc)); 131 errno = CNID_ERR_DB; 132 goto cleanup; 133 } 134 135 memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN); 136 rootinfo_data.data = buf; 137 rootinfo_data.size = ROOTINFO_DATALEN; 138 memcpy((char *)rootinfo_data.data +CNID_TYPE_OFS, &hint, sizeof(hint)); 139 if (setstamp) { 140 if (db_stamp(stamp, CNID_DEV_LEN) < 0) { 141 goto cleanup; 142 } 143 memcpy((char *)rootinfo_data.data +CNID_DEV_OFS, stamp, sizeof(stamp)); 144 } 145 146 147 switch (rc = cursor->c_put(cursor, &rootinfo_key, &rootinfo_data, flag)) { 148 case 0: 149 break; 150 default: 151 LOG(log_error, logtype_default, "cnid_add: Unable to update rootinfo: %s", db_strerror(rc)); 152 errno = CNID_ERR_DB; 153 goto cleanup; 154 } 155 if ((rc = cursor->c_close(cursor)) != 0) { 156 LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc)); 157 errno = CNID_ERR_DB; 158 return CNID_INVALID; 159 } 160 return hint; 161cleanup: 162 if ((rc = cursor->c_close(cursor)) != 0) { 163 LOG(log_error, logtype_default, "get_cnid: Unable to close cursor: %s", db_strerror(rc)); 164 return CNID_INVALID; 165 } 166 return CNID_INVALID; 167} 168 169/* ------------------------ */ 170cnid_t cnid_cdb_add(struct _cnid_db *cdb, const struct stat *st, 171 const cnid_t did, char *name, const size_t len, 172 cnid_t hint) 173{ 174 CNID_private *db; 175 DBT key, data; 176 cnid_t id; 177 int rc; 178 179 if (!cdb || !(db = cdb->_private) || !st || !name) { 180 errno = CNID_ERR_PARAM; 181 return CNID_INVALID; 182 } 183 184 /* Do a lookup. */ 185 id = cnid_cdb_lookup(cdb, st, did, name, len); 186 /* ... Return id if it is valid, or if Rootinfo is read-only. */ 187 if (id || (db->flags & CNIDFLAG_DB_RO)) { 188#ifdef DEBUG 189 LOG(log_debug9, logtype_default, "cnid_add: Looked up did %u, name %s as %u", ntohl(did), name, ntohl(id)); 190#endif 191 return id; 192 } 193 194 /* Initialize our DBT data structures. */ 195 memset(&key, 0, sizeof(key)); 196 memset(&data, 0, sizeof(data)); 197 198 if ((data.data = make_cnid_data(cdb->flags, st, did, name, len)) == NULL) { 199 LOG(log_error, logtype_default, "cnid_add: Path name is too long"); 200 errno = CNID_ERR_PATH; 201 return CNID_INVALID; 202 } 203 data.size = CNID_HEADER_LEN + len + 1; 204 205 if ((hint = get_cnid(db)) == 0) { 206 errno = CNID_ERR_DB; 207 return CNID_INVALID; 208 } 209 memcpy(data.data, &hint, sizeof(hint)); 210 211 key.data = &hint; 212 key.size = sizeof(hint); 213 214 /* Now we need to add the CNID data to the databases. */ 215 if ((rc = db->db_cnid->put(db->db_cnid, tid, &key, &data, DB_NOOVERWRITE))) { 216 if (rc == EINVAL) { 217 /* if we have a duplicate 218 * on cnid it's a fatal error. 219 * on dev:inode 220 * - leftover should have been delete before. 221 * - a second process already updated the db 222 * - it's a new file eg our file is already deleted and replaced 223 * on did:name leftover 224 */ 225 if (cnid_cdb_update(cdb, hint, st, did, name, len)) { 226 errno = CNID_ERR_DB; 227 return CNID_INVALID; 228 } 229 } 230 else { 231 LOG(log_error, logtype_default 232 , "cnid_add: Failed to add CNID for %s to database using hint %u: %s", 233 name, ntohl(hint), db_strerror(rc)); 234 errno = CNID_ERR_DB; 235 return CNID_INVALID; 236 } 237 } 238 239#ifdef DEBUG 240 LOG(log_debug9, logtype_default, "cnid_add: Returned CNID for did %u, name %s as %u", ntohl(did), name, ntohl(hint)); 241#endif 242 243 return hint; 244} 245 246/* cnid_cbd_getstamp */ 247/*-----------------------*/ 248int cnid_cdb_getstamp(struct _cnid_db *cdb, void *buffer, const size_t len) 249{ 250 DBT key, data; 251 int rc; 252 CNID_private *db; 253 254 if (!cdb || !(db = cdb->_private) || !buffer || !len) { 255 errno = CNID_ERR_PARAM; 256 return -1; 257 } 258 259 memset(buffer, 0, len); 260 memset(&key, 0, sizeof(key)); 261 memset(&data, 0, sizeof(data)); 262 263 key.data = ROOTINFO_KEY; 264 key.size = ROOTINFO_KEYLEN; 265 266 if (0 != (rc = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0 )) ) { 267 if (rc != DB_NOTFOUND) { 268 LOG(log_error, logtype_default, "cnid_lookup: Unable to get database stamp: %s", 269 db_strerror(rc)); 270 errno = CNID_ERR_DB; 271 return -1; 272 } 273 /* we waste a single ID here... */ 274 get_cnid(db); 275 memset(&key, 0, sizeof(key)); 276 memset(&data, 0, sizeof(data)); 277 key.data = ROOTINFO_KEY; 278 key.size = ROOTINFO_KEYLEN; 279 if (0 != (rc = db->db_cnid->get(db->db_cnid, NULL, &key, &data, 0 )) ) { 280 LOG(log_error, logtype_default, "cnid_getstamp: failed to get rootinfo: %s", 281 db_strerror(rc)); 282 errno = CNID_ERR_DB; 283 return -1; 284 } 285 } 286 287 memcpy(buffer, (char*)data.data + CNID_DEV_OFS, len); 288#ifdef DEBUG 289 LOG(log_debug9, logtype_cnid, "cnid_getstamp: Returning stamp"); 290#endif 291 return 0; 292} 293 294 295#endif /* CNID_BACKEND_CDB */ 296