1/* 2 * $Id: dbd_lookup.c,v 1.18 2010-01-19 14:57:11 franklahm Exp $ 3 * 4 * Copyright (C) Joerg Lenneis 2003 5 * Copyright (C) Frank Lahm 2009 6 * All Rights Reserved. See COPYING. 7 */ 8 9/* 10CNID salvation spec: 11general rule: better safe then sorry, so we always delete CNIDs and assign 12new ones in case of a lookup mismatch. afpd also sends us the CNID found 13in the adouble file (for AFPVOL_CACHE volumes). In certain cases we can 14use this hint to determince the right CNID. 15 16 17The lines... 18 19Id Did T Dev Inode Name 20============================ 21a b c d e name1 22--> 23f g h i h name2 24 25...are the expected results of certain operations. (f) is the speced CNID, in some 26cases it's only intermediate as described in the text and is overridden by another 27spec. 28 291) UNIX rename (via mv) or inode reusage(!) 30------------------------------------------- 31Name is possibly changed (rename case) but inode is the same. 32We should try to keep the CNID, but we cant, because inode reusage is probably 33much to frequent. 34 35rename: 3615 2 f 1 1 file 37--> 3815 x f 1 1 renamedfile 39 40inode reusage: 4115 2 f 1 1 file 42--> 4316 y f 1 1 inodereusagefile 44 45Result in dbd_lookup (-: not found, +: found): 46+ devino 47- didname 48 49Possible solution: 50None. Delete old data, file gets new CNID in both cases (rename and inode). 51If we got a hint and hint matches the CNID from devino we keep it and update 52the record. 53 542) UNIX mv from one folder to another 55---------------------------------------- 56Name is unchanged and inode stays the same, but DID is different. 57We should try to keep the CNID. 58 5915 2 f 1 1 file 60--> 6115 x f 1 1 file 62 63Result in dbd_lookup: 64+ devino 65- didname 66 67Possible solution: 68strcmp names, if they match keep CNID. Unfortunately this also can't be 69distinguished from a new file with a reused inode. So me must assign 70a new CNID. 71If we got a hint and hint matches the CNID from devino we keep it and update 72the record. 73 743) Restore from backup ie change of inode number -- or emacs 75------------------------------------------------------------ 76 7715 2 f 1 1 file 78--> 7915 2 f 1 2 file 80 81Result in dbd_lookup: 82- devino 83+ didname 84 85Possible fixup solution: 86test-suite test235 tests and ensures that the CNID is _changed_. The reason for 87this is somewhat lost in time, but nevertheless we believe our test suite. 88 89Similar things happen with emas: emacs uses a backup file (file~). When saving 90because of inode reusage of the fs, both files most likely exchange inodes. 91 9215 2 f 1 1 file 9316 2 f 1 2 file~ 94--> this would be nice: 9515 2 f 1 2 file 9616 2 f 1 1 file~ 97--> but for the reasons described above we must implement 9817 2 f 1 2 file 9918 2 f 1 1 file~ 100 101Result in dbd_lookup for the emacs case: 102+ devino --> CNID: 16 103+ didname -> CNID: 15 104devino search and didname search result in different CNIDs !! 105 106Possible fixup solution: 107to be safe we must assign new CNIDs to both files. 108*/ 109 110#ifdef HAVE_CONFIG_H 111#include "config.h" 112#endif /* HAVE_CONFIG_H */ 113 114 115#include <stdio.h> 116#include <string.h> 117#include <sys/param.h> 118#include <errno.h> 119#include <netatalk/endian.h> 120#include <atalk/logger.h> 121#include <atalk/cnid_dbd_private.h> 122 123#include "pack.h" 124#include "dbif.h" 125#include "dbd.h" 126 127/* 128 * This returns the CNID corresponding to a particular file. It will also fix 129 * up the database if there's a problem. 130 */ 131 132int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag) 133{ 134 unsigned char *buf; 135 DBT key, devdata, diddata; 136 int devino = 1, didname = 1; 137 int rc; 138 cnid_t id_devino, id_didname; 139 u_int32_t type_devino = (unsigned)-1; 140 u_int32_t type_didname = (unsigned)-1; 141 int update = 0; 142 143 memset(&key, 0, sizeof(key)); 144 memset(&diddata, 0, sizeof(diddata)); 145 memset(&devdata, 0, sizeof(devdata)); 146 147 rply->namelen = 0; 148 rply->cnid = 0; 149 150 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START"); 151 152 buf = pack_cnid_data(rqst); 153 154 /* Look for a CNID. We have two options: dev/ino or did/name. If we 155 only get a match in one of them, that means a file has moved. */ 156 key.data = buf + CNID_DEVINO_OFS; 157 key.size = CNID_DEVINO_LEN; 158 159 if ((rc = dbif_get(dbd, DBIF_IDX_DEVINO, &key, &devdata, 0)) < 0) { 160 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s", 161 ntohl(rqst->did), rqst->name); 162 rply->result = CNID_DBD_RES_ERR_DB; 163 return -1; 164 } 165 if (rc == 0) { 166 devino = 0; 167 } 168 else { 169 memcpy(&id_devino, devdata.data, sizeof(rply->cnid)); 170 memcpy(&type_devino, (char *)devdata.data +CNID_TYPE_OFS, sizeof(type_devino)); 171 type_devino = ntohl(type_devino); 172 } 173 174 key.data = buf + CNID_DID_OFS; 175 key.size = CNID_DID_LEN + rqst->namelen + 1; 176 177 if ((rc = dbif_get(dbd, DBIF_IDX_DIDNAME, &key, &diddata, 0)) < 0) { 178 LOG(log_error, logtype_cnid, "dbd_lookup: Unable to get CNID %u, name %s", 179 ntohl(rqst->did), rqst->name); 180 rply->result = CNID_DBD_RES_ERR_DB; 181 return -1; 182 } 183 if (rc == 0) { 184 didname = 0; 185 } 186 else { 187 memcpy(&id_didname, diddata.data, sizeof(rply->cnid)); 188 memcpy(&type_didname, (char *)diddata.data +CNID_TYPE_OFS, sizeof(type_didname)); 189 type_didname = ntohl(type_didname); 190 } 191 192 LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}", 193 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname); 194 195 /* Have we found anything at all ? */ 196 if (!devino && !didname) { 197 /* nothing found */ 198 LOG(log_debug, logtype_cnid, "dbd_lookup: name: '%s', did: %u, dev/ino: 0x%llx/0x%llx is not in the CNID database", 199 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); 200 rply->result = CNID_DBD_RES_NOTFOUND; 201 return 1; 202 } 203 204 /* Check for type (file/dir) mismatch */ 205 if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) { 206 207 if (devino && (type_devino != rqst->type)) { 208 /* one is a dir one is a file, remove from db */ 209 210 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino", 211 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); 212 213 if (! roflag) { 214 rqst->cnid = id_devino; 215 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID); 216 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO); 217 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME); 218 if (rc < 0) { 219 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 220 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); 221 return -1; 222 } 223 } 224 } 225 226 if (didname && (type_didname != rqst->type)) { 227 /* same: one is a dir one is a file, remove from db */ 228 229 LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname", 230 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); 231 232 if (! roflag) { 233 rqst->cnid = id_didname; 234 rc = dbd_delete(dbd, rqst, rply, DBIF_CNID); 235 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO); 236 rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME); 237 if (rc < 0) { 238 LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 239 rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); 240 return -1; 241 } 242 } 243 } 244 245 rply->result = CNID_DBD_RES_NOTFOUND; 246 return 1; 247 } 248 249 if (devino && didname && id_devino == id_didname) { 250 /* everything is fine */ 251 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID: %u", 252 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, htonl(id_didname)); 253 rply->cnid = id_didname; 254 rply->result = CNID_DBD_RES_OK; 255 return 1; 256 } 257 258 if (devino && didname && id_devino != id_didname) { 259 /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */ 260 LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u", 261 ntohl(rqst->did), rqst->name, ntohl(id_didname), 262 (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino)); 263 264 if (! roflag) { 265 rqst->cnid = id_devino; 266 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) 267 return -1; 268 269 rqst->cnid = id_didname; 270 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) 271 return -1; 272 } 273 rply->result = CNID_DBD_RES_NOTFOUND; 274 return 1; 275 } 276 277 if ( ! didname) { 278 LOG(log_debug, logtype_cnid, "dbd_lookup(CNID hint: %u, DID:%u, \"%s\", 0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode", 279 ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); 280 if (rqst->cnid == id_devino) { 281 LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)"); 282 update = 1; 283 } else { 284 if ( ! roflag) { 285 rqst->cnid = id_devino; 286 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) 287 return -1; 288 } 289 rply->result = CNID_DBD_RES_NOTFOUND; 290 return 1; 291 } 292 } 293 294 if ( ! devino) { 295 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino", 296 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino); 297 if ( ! roflag) { 298 rqst->cnid = id_didname; 299 if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0) 300 return -1; 301 } 302 rply->result = CNID_DBD_RES_NOTFOUND; 303 return 1; 304 } 305 306 /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/ 307 if (!update || roflag) { 308 rply->result = CNID_DBD_RES_NOTFOUND; 309 return 1; 310 } 311 312 /* Fix up the database */ 313 rc = dbd_update(dbd, rqst, rply); 314 if (rc >0) { 315 rply->cnid = rqst->cnid; 316 } 317 318 LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): Got CNID (needed update): %u", 319 ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(rply->cnid)); 320 321 return rc; 322} 323