]> wimlib.net Git - wimlib/blobdiff - src/update_image.c
rename_wim_path(): Fix journaling of name change
[wimlib] / src / update_image.c
index 70038d42d0e605bbea400ef9a9406bf2779a88b2..06a6ad30261434a3a6a0107602b29831bb2b07ef 100644 (file)
@@ -949,6 +949,19 @@ free_dentry_full_path(struct wim_dentry *dentry, void *_ignore)
        return 0;
 }
 
+/* Is @d1 a (possibly nonproper) ancestor of @d2?  */
+static bool
+is_ancestor(struct wim_dentry *d1, struct wim_dentry *d2)
+{
+       for (;;) {
+               if (d2 == d1)
+                       return true;
+               if (dentry_is_root(d2))
+                       return false;
+               d2 = d2->parent;
+       }
+}
+
 /* Rename a file or directory in the WIM.
  *
  * This returns a -errno value.
@@ -1003,29 +1016,29 @@ rename_wim_path(WIMStruct *wim, const tchar *from, const tchar *to,
                        return -ENOTDIR;
        }
 
+       /* @src can't be an ancestor of @dst.  Otherwise we're unlinking @src
+        * from the tree and creating a loop...  */
+       if (is_ancestor(src, parent_of_dst))
+               return -EBUSY;
+
        if (j) {
+               if (dst)
+                       if (journaled_unlink(j, dst))
+                               return -ENOMEM;
+               if (journaled_unlink(j, src))
+                       return -ENOMEM;
                if (journaled_change_name(j, src, path_basename(to)))
                        return -ENOMEM;
+               if (journaled_link(j, src, parent_of_dst))
+                       return -ENOMEM;
        } else {
                ret = dentry_set_name(src, path_basename(to));
                if (ret)
                        return -ENOMEM;
-       }
-       if (dst) {
-               if (j) {
-                       if (journaled_unlink(j, dst))
-                               return -ENOMEM;
-               } else {
+               if (dst) {
                        unlink_dentry(dst);
                        free_dentry_tree(dst, wim->lookup_table);
                }
-       }
-       if (j) {
-               if (journaled_unlink(j, src))
-                       return -ENOMEM;
-               if (journaled_link(j, src, parent_of_dst))
-                       return -ENOMEM;
-       } else {
                unlink_dentry(src);
                dentry_add_child(parent_of_dst, src);
        }
@@ -1059,6 +1072,9 @@ execute_rename_command(struct update_command_journal *j,
                        ret = WIMLIB_ERR_NOTDIR;
                        break;
                case ENOTEMPTY:
+               case EBUSY:
+                       /* XXX: EBUSY is returned when the rename would create a
+                        * loop.  It maybe should have its own error code.  */
                        ret = WIMLIB_ERR_NOTEMPTY;
                        break;
                case EISDIR: