+/*
+ Check CNID for a file/dir, both from db and from ad-file.
+ For detailed specs see intro.
+
+ @return Correct CNID of object or CNID_INVALID (ie 0) on error
+*/
+static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags)
+{
+ int ret;
+ cnid_t db_cnid, ad_cnid;
+ struct adouble ad;
+
+ /* Force checkout every X items */
+ static int cnidcount = 0;
+ cnidcount++;
+ if (cnidcount > 10000) {
+ cnidcount = 0;
+ if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) {
+ dbd_log(LOGSTD, "Error checkpointing!");
+ return CNID_INVALID;
+ }
+ }
+
+ /* Get CNID from ad-file if volume is using AFPVOL_CACHE */
+ ad_cnid = 0;
+ if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
+ if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
+
+ if (dbd_flags & DBD_FLAGS_CLEANUP)
+ return CNID_INVALID;
+
+ dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno));
+ return CNID_INVALID;
+ }
+
+ if (dbd_flags & DBD_FLAGS_FORCE) {
+ ad_cnid = ad_forcegetid(&ad);
+ /* This ensures the changed stamp is written */
+ ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp);
+ ad_flush(&ad);
+ }
+ else
+ ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp);
+
+ if (ad_cnid == 0)
+ dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name);
+ else
+ dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid));
+
+ ad_close_metadata(&ad);
+ }
+
+ /* Get CNID from database */
+
+ /* Prepare request data */
+ memset(&rqst, 0, sizeof(struct cnid_dbd_rqst));
+ memset(&rply, 0, sizeof(struct cnid_dbd_rply));
+ rqst.did = did;
+ rqst.cnid = ad_cnid;
+ if ( ! (myvolinfo->v_flags & AFPVOL_NODEV))
+ rqst.dev = st->st_dev;
+ rqst.ino = st->st_ino;
+ rqst.type = S_ISDIR(st->st_mode)?1:0;
+ rqst.name = (char *)name;
+ rqst.namelen = strlen(name);
+
+ /* Query the database */
+ ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0);
+ if (dbif_txn_close(dbd, ret) != 0)
+ return CNID_INVALID;
+ if (rply.result == CNID_DBD_RES_OK) {
+ db_cnid = rply.cnid;
+ } else if (rply.result == CNID_DBD_RES_NOTFOUND) {
+ if ( ! (dbd_flags & DBD_FLAGS_FORCE))
+ dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name);
+ db_cnid = 0;
+ } else {
+ dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name);
+ db_cnid = 0;
+ }
+
+ /* Compare results from both CNID searches */
+ if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) {
+ /* Everything is fine */
+ return db_cnid;
+ } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) {
+ /* Mismatch ? Delete both from db and re-add data from file */
+ dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid));
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ rqst.cnid = db_cnid;
+ ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
+ if (dbif_txn_close(dbd, ret) != 0)
+ return CNID_INVALID;
+
+ rqst.cnid = ad_cnid;
+ ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID);
+ if (dbif_txn_close(dbd, ret) != 0)
+ return CNID_INVALID;
+
+ ret = dbd_rebuild_add(dbd, &rqst, &rply);
+ if (dbif_txn_close(dbd, ret) != 0)
+ return CNID_INVALID;
+ }
+ return ad_cnid;
+ } else if (ad_cnid && (db_cnid == 0)) {
+ /* in ad-file but not in db */
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ /* Ensure the cnid from the ad-file is not already occupied by another file */
+ dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied",
+ ntohl(ad_cnid));
+
+ rqst.cnid = ad_cnid;
+ ret = dbd_resolve(dbd, &rqst, &rply);
+ if (ret == CNID_DBD_RES_OK) {
+ /* Occupied! Choose another, update ad-file */
+ ret = dbd_add(dbd, &rqst, &rply, 1);
+ if (dbif_txn_close(dbd, ret) != 0)
+ return CNID_INVALID;
+ db_cnid = rply.cnid;
+ dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
+
+ if ((myvolinfo->v_flags & AFPVOL_CACHE)
+ && ADFILE_OK
+ && ( ! (dbd_flags & DBD_FLAGS_SCAN))) {
+ dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
+ cwdbuf, name, ntohl(db_cnid));
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
+ if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
+ dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
+ cwdbuf, name, strerror(errno));
+ return CNID_INVALID;
+ }
+ ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
+ ad_flush(&ad);
+ ad_close_metadata(&ad);
+ }
+ return db_cnid;
+ }
+
+ dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u",
+ cwdbuf, name, ntohl(ad_cnid));
+ rqst.cnid = ad_cnid;
+ ret = dbd_rebuild_add(dbd, &rqst, &rply);
+ if (dbif_txn_close(dbd, ret) != 0)
+ return CNID_INVALID;
+ }
+ return ad_cnid;
+ } else if ((db_cnid == 0) && (ad_cnid == 0)) {
+ /* No CNID at all, we clearly have to allocate a fresh one... */
+ /* Note: the next test will use this new CNID too! */
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ /* add to db */
+ ret = dbd_add(dbd, &rqst, &rply, 1);
+ if (dbif_txn_close(dbd, ret) != 0)
+ return CNID_INVALID;
+ db_cnid = rply.cnid;
+ dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid));
+ }
+ }
+
+ if ((ad_cnid == 0) && db_cnid) {
+ /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */
+ if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) {
+ if ( ! (dbd_flags & DBD_FLAGS_SCAN)) {
+ dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file",
+ cwdbuf, name, ntohl(db_cnid));
+ ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options);
+ if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) {
+ dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s",
+ cwdbuf, name, strerror(errno));
+ return CNID_INVALID;
+ }
+ ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp);
+ ad_flush(&ad);
+ ad_close_metadata(&ad);
+ }
+ }
+ return db_cnid;
+ }
+
+ return CNID_INVALID;
+}
+
+/*
+ This is called recursively for all dirs.
+ volroot=1 means we're in the volume root dir, 0 means we aren't.
+ We use this when checking for netatalk private folders like .AppleDB.
+ did is our parents CNID.
+*/