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 20static 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 41unsigned 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 */ 74static 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 103abort: 104 return -1; 105} 106 107/* ----------------------- */ 108static 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; 147cleanup: 148 tdb_chainunlock(db->tdb_didname, rootinfo_key); 149 return CNID_INVALID; 150} 151 152 153/* ------------------------ */ 154cnid_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 211