]> arthur.barton.de Git - netatalk.git/blobdiff - etc/cnid_dbd/dbd_lookup.c
New MySQL CNID backend
[netatalk.git] / etc / cnid_dbd / dbd_lookup.c
index 45119da8fcdd97e020bd77228bfe3162e3473fa6..9ff4e411d356115829d7f3b6959168884d367047 100644 (file)
@@ -1,5 +1,4 @@
 /*
- * $Id: dbd_lookup.c,v 1.11 2009-11-25 14:59:15 franklahm Exp $
  *
  * Copyright (C) Joerg Lenneis 2003
  * Copyright (C) Frank Lahm 2009
@@ -8,8 +7,10 @@
 
 /* 
 CNID salvation spec:
-general rule: better safe then sorry, so we always delete CNIDs and assing
-new ones in case of a lookup mismatch.
+general rule: better safe then sorry, so we always delete CNIDs and assign
+new ones in case of a lookup mismatch. afpd also sends us the CNID found
+in the adouble file. In certain cases we can use this hint to determince
+the right CNID.
 
 
 The lines...
@@ -46,6 +47,8 @@ Result in dbd_lookup (-: not found, +: found):
 
 Possible solution:
 None. Delete old data, file gets new CNID in both cases (rename and inode).
+If we got a hint and hint matches the CNID from devino we keep it and update
+the record.
 
 2) UNIX mv from one folder to another
 ----------------------------------------
@@ -61,10 +64,15 @@ Result in dbd_lookup:
 - didname
 
 Possible solution:
-strcmp names, if they match keep CNID.
+strcmp names, if they match keep CNID. Unfortunately this also can't be
+distinguished from a new file with a reused inode. So me must assign
+a new CNID.
+If we got a hint and hint matches the CNID from devino we keep it and update
+the record.
+
+3) Restore from backup ie change of inode number -- or emacs
+------------------------------------------------------------
 
-3) Restore from backup
-----------------------
 15  2   f   1   1       file
 -->
 15  2   f   1   2       file
@@ -74,29 +82,18 @@ Result in dbd_lookup:
 + didname
 
 Possible fixup solution:
-Update.
-
-4) emacs
---------
-emacs uses a backup file (file~). When saving because of inode reusage of the fs,
-both files exchange inodes.
-
-General case for inode reusage:
-15  2   f   1   1       file
--->
-16  2   f   1   1       new_file_with_reused_inode
+test-suite test235 tests and ensures that the CNID is _changed_. The reason for
+this is somewhat lost in time, but nevertheless we believe our test suite.
 
-Result in dbd_lookup:
-+ devino
-- didname
+Similar things happen with emas: emacs uses a backup file (file~). When saving
+because of inode reusage of the fs, both files most likely exchange inodes.
 
-Emacs case:
 15  2   f   1   1       file
 16  2   f   1   2       file~
 --> this would be nice:
 15  2   f   1   2       file
 16  2   f   1   1       file~
---> but because we must follow the general case you get this:
+--> but for the reasons described above we must implement
 17  2   f   1   2       file
 18  2   f   1   1       file~
 
@@ -106,7 +103,7 @@ Result in dbd_lookup for the emacs case:
 devino search and didname search result in different CNIDs !!
 
 Possible fixup solution:
-to be safe we must implement the general case, sorry emacs.
+to be safe we must assign new CNIDs to both files.
 */
 
 #ifdef HAVE_CONFIG_H
@@ -118,9 +115,11 @@ to be safe we must implement the general case, sorry emacs.
 #include <string.h>
 #include <sys/param.h>
 #include <errno.h>
-#include <netatalk/endian.h>
+#include <arpa/inet.h>
+
 #include <atalk/logger.h>
-#include <atalk/cnid_dbd_private.h>
+#include <atalk/cnid_bdb_private.h>
+#include <atalk/cnid.h>
 
 #include "pack.h"
 #include "dbif.h"
@@ -131,7 +130,7 @@ to be safe we must implement the general case, sorry emacs.
  *  up the database if there's a problem.
  */
 
-int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply, int roflag)
+int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply)
 {
     unsigned char *buf;
     DBT key, devdata, diddata;
@@ -148,6 +147,8 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
 
     rply->namelen = 0;
     rply->cnid = 0;
+
+    LOG(log_maxdebug, logtype_cnid, "dbd_lookup(): START");
     
     buf = pack_cnid_data(rqst); 
 
@@ -189,6 +190,9 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
         type_didname = ntohl(type_didname);
     }
 
+    LOG(log_maxdebug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx) {devino: %u, didname: %u}", 
+        rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, devino, didname);
+
     /* Have we found anything at all ? */
     if (!devino && !didname) {  
         /* nothing found */
@@ -199,20 +203,42 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
     }
 
     /* Check for type (file/dir) mismatch */
-    if (devino && (type_devino != rqst->type)) {
-        /* one is a dir one is a file, remove from db */
-        rqst->cnid = id_devino;
-        if (! roflag)
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+    if ((devino && (type_devino != rqst->type)) || (didname && (type_didname != rqst->type))) {
+
+        if (devino && (type_devino != rqst->type)) {
+            /* one is a dir one is a file, remove from db */
+
+            LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for devino", 
+                rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+
+            rqst->cnid = id_devino;
+            rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
+            if (rc < 0) {
+                LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for devino", 
+                    rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
                 return -1;
-        rply->result = CNID_DBD_RES_NOTFOUND;
-        return 1;
-    } else if (didname && (type_didname != rqst->type)) {
-        /* same: one is a dir one is a file, remove from db */
-        rqst->cnid = id_didname;
-        if (! roflag)
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            }
+        }
+
+        if (didname && (type_didname != rqst->type)) {
+            /* same: one is a dir one is a file, remove from db */
+
+            LOG(log_debug, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): type mismatch for didname", 
+                rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+
+            rqst->cnid = id_didname;
+            rc = dbd_delete(dbd, rqst, rply, DBIF_CNID);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DEVINO);
+            rc += dbd_delete(dbd, rqst, rply, DBIF_IDX_DIDNAME);
+            if (rc < 0) {
+                LOG(log_error, logtype_cnid, "dbd_lookup(name:'%s', did:%u, dev/ino:0x%llx/0x%llx): error deleting type mismatch for didname", 
+                    rqst->name, ntohl(rqst->did), (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
                 return -1;
+            }
+        }
+
         rply->result = CNID_DBD_RES_NOTFOUND;
         return 1;
     }
@@ -227,38 +253,35 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
     }
 
     if (devino && didname && id_devino != id_didname) {
-        /* CNIDs don't match, something of a worst case! */
+        /* CNIDs don't match, something of a worst case, or possibly 3) emacs! */
         LOG(log_debug, logtype_cnid, "dbd_lookup: CNID mismatch: (DID:%u/'%s') --> %u , (0x%llx/0x%llx) --> %u",
             ntohl(rqst->did), rqst->name, ntohl(id_didname),
             (unsigned long long)rqst->dev, (unsigned long long)rqst->ino, ntohl(id_devino));
 
-        /* Something like 5), the emacs case (see above), remove it all */
-        if (! roflag) {
-            rqst->cnid = id_devino;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
+        rqst->cnid = id_devino;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
+
+        rqst->cnid = id_didname;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
 
-            rqst->cnid = id_didname;
-            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                return -1;
-        }
         rply->result = CNID_DBD_RES_NOTFOUND;
         return 1;
     }
 
     if ( ! didname) {
-        LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: server side rename oder reused inode",
-            ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
-        rqst->cnid = id_devino;
-        /* Case 2) ? */
-        if (strcmp(rqst->name, (char *)devdata.data + CNID_NAME_OFS) == 0) {
-            LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv from one dir to another");
+        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",
+            ntohl(rqst->cnid), ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
+        if (rqst->cnid == id_devino) {
+            LOG(log_debug, logtype_cnid, "dbd_lookup: server side mv (with resource fork)");
             update = 1;
         } else {
-            if ( ! roflag)
-                if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
-                    return -1;
+            rqst->cnid = id_devino;
+            if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+                return -1;
             rply->result = CNID_DBD_RES_NOTFOUND;
+            rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
             return 1;
         }
     }
@@ -267,11 +290,15 @@ int dbd_lookup(DBD *dbd, struct cnid_dbd_rqst *rqst, struct cnid_dbd_rply *rply,
         LOG(log_debug, logtype_cnid, "dbd_lookup(DID:%u/'%s',0x%llx/0x%llx): CNID resolve problem: changed dev/ino",
             ntohl(rqst->did), rqst->name, (unsigned long long)rqst->dev, (unsigned long long)rqst->ino);
         rqst->cnid = id_didname;
-        update = 1;
+        if (dbd_delete(dbd, rqst, rply, DBIF_CNID) < 0)
+            return -1;
+        rply->result = CNID_DBD_RES_NOTFOUND;
+        rqst->cnid = CNID_INVALID; /* invalidate CNID hint */
+        return 1;
     }
 
     /* This is also a catch all if we've forgot to catch some possibility with the preceding ifs*/
-    if (!update || roflag) {
+    if (!update) {
         rply->result = CNID_DBD_RES_NOTFOUND;
         return 1;
     }