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