We're seeing failures where a file is renamed under pseudo but an
access appears to be made to the old filename before the OP_RENAME has
hit the database but after the real_rename has applied in the kernel.

This is effectively the MAY_UNLINK problem for the original filename. There
were protections for the newpath but not the oldpath.

To try and avoid these aborts(), mark the original path as MAY_UNLINK however
we need to be careful not to trigger the DID_UNLINK but instead update
the deleting entry in the database as the rename completes. To do this,
we no clear the deleting flag during the database rename operation in SQL.

Also, we have to stop removing the by_ino matches where by_ino.deleting is
set, else we may lose the renamed file's attributes that way.

Signed-off-by: Richard Purdie <richard.pur...@linuxfoundation.org>
---
 ports/unix/guts/rename.c   | 35 ++++++++++++++++++++++-------------
 ports/unix/guts/renameat.c | 34 +++++++++++++++++++++-------------
 pseudo.c                   | 11 +++++------
 pseudo_db.c                |  4 ++--
 4 files changed, 50 insertions(+), 34 deletions(-)

diff --git a/ports/unix/guts/rename.c b/ports/unix/guts/rename.c
index 7e4a499..80bbf41 100644
--- a/ports/unix/guts/rename.c
+++ b/ports/unix/guts/rename.c
@@ -13,7 +13,8 @@
        int oldrc, newrc;
        int save_errno;
        int old_db_entry = 0;
-       int may_unlinked = 0;
+       int may_unlink_new = 0;
+       int may_unlink_old = 0;
 
        pseudo_debug(PDBGF_OP, "rename: %s->%s\n",
                oldpath ? oldpath : "<nil>",
@@ -43,28 +44,36 @@
        /* as with unlink, we have to mark that the file may get deleted */
        msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, -1, newpath, newrc ? NULL 
: &newbuf);
        if (msg && msg->result == RESULT_SUCCEED)
-               may_unlinked = 1;
+               may_unlink_new = 1;
+        /* oldpath is also likely to disappear. Something could call stat() 
after 
+           real_rename so we need to mark as MAY_UNLINK too */
+       msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, -1, oldpath, oldrc ? NULL 
: &oldbuf);
+       if (msg && msg->result == RESULT_SUCCEED)
+               may_unlink_old = 1;
+
        msg = pseudo_client_op(OP_STAT, 0, -1, -1, oldpath, oldrc ? NULL : 
&oldbuf);
        if (msg && msg->result == RESULT_SUCCEED)
                old_db_entry = 1;
        rc = real_rename(oldpath, newpath);
        save_errno = errno;
-       if (may_unlinked) {
-               if (rc == -1) {
-                       /* since we failed, that wasn't really unlinked -- put
-                        * it back.
-                        */
-                       pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, newpath, 
&newbuf);
-               } else {
-                       /* confirm that the file was removed */
-                       pseudo_client_op(OP_DID_UNLINK, 0, -1, -1, newpath, 
&newbuf);
-               }
-       }
+
        if (rc == -1) {
+               /* since we failed, that wasn't really unlinked -- put
+                * it back.
+                */
+               if (may_unlink_new)
+                       pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, newpath, 
&newbuf);
+               if (may_unlink_old)
+                       pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, -1, oldpath, 
&oldbuf);
                /* and we're done. */
                errno = save_errno;
                return rc;
        }
+
+       /* confirm that the file was removed */
+       if (may_unlink_new)     
+               pseudo_client_op(OP_DID_UNLINK, 0, -1, -1, newpath, &newbuf);
+       /* OP_DID_UNLINK for oldpath is handled by the server */
        save_errno = errno;
 
        /* rename(3) is not mv(1).  rename(file, dir) fails; you must provide
diff --git a/ports/unix/guts/renameat.c b/ports/unix/guts/renameat.c
index 8064818..5ac63f9 100644
--- a/ports/unix/guts/renameat.c
+++ b/ports/unix/guts/renameat.c
@@ -13,7 +13,8 @@
        int oldrc, newrc;
        int save_errno;
        int old_db_entry = 0;
-       int may_unlinked = 0;
+       int may_unlink_new = 0;
+       int may_unlink_old = 0;
 
        pseudo_debug(PDBGF_FILE, "renameat: %d,%s->%d,%s\n",
                olddirfd, oldpath ? oldpath : "<nil>",
@@ -55,29 +56,36 @@
        /* as with unlink, we have to mark that the file may get deleted */
        msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, newdirfd, newpath, newrc ? 
NULL : &newbuf);
        if (msg && msg->result == RESULT_SUCCEED)
-               may_unlinked = 1;
+               may_unlink_new = 1;
+        /* oldpath is also likely to disappear. Something could call stat() 
after 
+           real_rename so we need to mark as MAY_UNLINK too */
+       msg = pseudo_client_op(OP_MAY_UNLINK, 0, -1, olddirfd, oldpath, oldrc ? 
NULL : &oldbuf);
+       if (msg && msg->result == RESULT_SUCCEED)
+               may_unlink_old = 1;
+
        msg = pseudo_client_op(OP_STAT, 0, -1, olddirfd, oldpath, oldrc ? NULL 
: &oldbuf);
        if (msg && msg->result == RESULT_SUCCEED)
                old_db_entry = 1;
 
        rc = real_renameat(olddirfd, oldpath, newdirfd, newpath);
        save_errno = errno;
-       if (may_unlinked) {
-               if (rc == -1) {
-                       /* since we failed, that wasn't really unlinked -- put
-                        * it back.
-                        */
-                       pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, newdirfd, 
newpath, &newbuf);
-               } else {
-                       /* confirm that the file was removed */
-                       pseudo_client_op(OP_DID_UNLINK, 0, -1, newdirfd, 
newpath, &newbuf);
-               }
-       }
        if (rc == -1) {
+               /* since we failed, that wasn't really unlinked -- put
+                * it back.
+                */
+               if (may_unlink_new)
+                       pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, newdirfd, 
newpath, &newbuf);
+               if (may_unlink_old)
+                       pseudo_client_op(OP_CANCEL_UNLINK, 0, -1, olddirfd, 
oldpath, &oldbuf);
                /* and we're done. */
                errno = save_errno;
                return rc;
        }
+
+       /* confirm that the file was removed */
+       if (may_unlink_new)
+               pseudo_client_op(OP_DID_UNLINK, 0, -1, newdirfd, newpath, 
&newbuf);
+       /* OP_DID_UNLINK for oldpath is handled by the server */
        save_errno = errno;
 
        /* rename(3) is not mv(1).  rename(file, dir) fails; you must provide
diff --git a/pseudo.c b/pseudo.c
index f2e2f87..528fe1b 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -688,14 +688,13 @@ pseudo_op(pseudo_msg_t *msg, const char *program, const 
char *tag, char **respon
                                break;
                        }
                        if (mismatch) {
-                               /* a mismatch, but we were planning to delete
-                                * the file, so it must have gotten deleted
-                                * already.
-                                */
                                if (by_ino.deleting != 0) {
-                                       pseudo_debug(PDBGF_FILE, "inode 
mismatch for '%s' -- old one was marked for deletion, deleting.\n",
+                                       /* a mismatch, likely a race but we 
were planning to
+                                        * delete the file, or are in the 
middle of a rename
+                                        * so continue, need the old entry for 
a rename.
+                                        */
+                                       pseudo_debug(PDBGF_FILE, "inode 
mismatch for '%s' -- old one was marked for deletion.\n",
                                                msg->path);
-                                       pdb_did_unlink_file(path_by_ino, 
&by_ino, by_ino.deleting);
                                } else {
                                        pseudo_diag("path mismatch [%d link%s]: 
ino %llu db '%s' req '%s'.\n",
                                                msg->nlink,
diff --git a/pseudo_db.c b/pseudo_db.c
index ebf6e08..6c48cc8 100644
--- a/pseudo_db.c
+++ b/pseudo_db.c
@@ -2020,8 +2020,8 @@ int
 pdb_rename_file(const char *oldpath, pseudo_msg_t *msg) {
        static sqlite3_stmt *update_exact, *update_sub;
        int rc;
-       char *sql_update_exact = "UPDATE files SET path = ? WHERE path = ?;";
-       char *sql_update_sub = "UPDATE files SET path = replace(path, ?, ?) "
+       char *sql_update_exact = "UPDATE files SET path = ?, deleting = 0 WHERE 
path = ?;";
+       char *sql_update_sub = "UPDATE files SET path = replace(path, ?, ?), 
deleting = 0 "
                               "WHERE (path > (? || '/') AND path < (? || 
'0'));";
 
        if (!file_db && get_dbs()) {
-- 
2.27.0

-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#147972): 
https://lists.openembedded.org/g/openembedded-core/message/147972
Mute This Topic: https://lists.openembedded.org/mt/80563056/21656
Group Owner: openembedded-core+ow...@lists.openembedded.org
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to