Repository: lucy
Updated Branches:
  refs/heads/master 291a32693 -> 7a64a288c


Use Windows API to delete and rename files

Delete files on Windows by calling CreateFile with
FILE_FLAG_DELETE_ON_CLOSE and closing the handle immediately. Unlike
DeleteFile, this allows files opened with FILE_SHARE_DELETE to be
(eventually) deleted.

Part of LUCY-324.


Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/3b1b63e8
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/3b1b63e8
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/3b1b63e8

Branch: refs/heads/master
Commit: 3b1b63e8d8f7fcdf30d4c2af7c9ee519c662569a
Parents: de646e6
Author: Nick Wellnhofer <[email protected]>
Authored: Sun Mar 5 14:21:29 2017 +0100
Committer: Nick Wellnhofer <[email protected]>
Committed: Sun Mar 5 16:29:44 2017 +0100

----------------------------------------------------------------------
 core/Lucy/Store/FSFileHandle.c |   4 +-
 core/Lucy/Store/FSFolder.c     | 125 ++++++++++++++++++++++++++++--------
 2 files changed, 102 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/3b1b63e8/core/Lucy/Store/FSFileHandle.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Store/FSFileHandle.c b/core/Lucy/Store/FSFileHandle.c
index fff9175..12769ec 100644
--- a/core/Lucy/Store/FSFileHandle.c
+++ b/core/Lucy/Store/FSFileHandle.c
@@ -388,15 +388,17 @@ S_init(FSFileHandleIVARS *ivars, String *path, uint32_t 
flags) {
     DWORD desired_access       = flags & FH_READ_ONLY
                                  ? GENERIC_READ
                                  : GENERIC_WRITE;
+    DWORD share_mode           = FILE_SHARE_READ;
     DWORD creation_disposition = flags & FH_CREATE
                                  ? flags & FH_EXCLUSIVE
                                    ? CREATE_NEW
                                    : OPEN_ALWAYS
                                  : OPEN_EXISTING;
 
+    share_mode |= FILE_SHARE_DELETE;
     char *path_ptr = Str_To_Utf8(path);
     HANDLE handle
-        = CreateFileA(path_ptr, desired_access, FILE_SHARE_READ, NULL,
+        = CreateFileA(path_ptr, desired_access, share_mode, NULL,
                       creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
     FREEMEM(path_ptr);
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3b1b63e8/core/Lucy/Store/FSFolder.c
----------------------------------------------------------------------
diff --git a/core/Lucy/Store/FSFolder.c b/core/Lucy/Store/FSFolder.c
index d4d7f41..a1caf80 100644
--- a/core/Lucy/Store/FSFolder.c
+++ b/core/Lucy/Store/FSFolder.c
@@ -78,9 +78,17 @@ S_is_absolute(String *path);
 static String*
 S_absolutify(String *path);
 
+// Rename a directory or file.
+static bool
+S_rename(const char *from_path, const char *to_path);
+
 // Create a hard link.
 static bool
-S_hard_link(char *from_path, char *to_path);
+S_hard_link(const char *from_path, const char *to_path);
+
+// Delete a directory or file.
+static bool
+S_delete(const char *path);
 
 FSFolder*
 FSFolder_new(String *path) {
@@ -179,13 +187,10 @@ FSFolder_Local_Is_Directory_IMP(FSFolder *self, String 
*name) {
 
 bool
 FSFolder_Rename_IMP(FSFolder *self, String* from, String *to) {
+    // TODO: Update Folder entries.
     char *from_path = S_fullpath_ptr(self, from);
     char *to_path   = S_fullpath_ptr(self, to);
-    bool  retval    = !rename(from_path, to_path);
-    if (!retval) {
-        ErrMsg_set_with_errno("rename from '%s' to '%s' failed",
-                              from_path, to_path);
-    }
+    bool retval = S_rename(from_path, to_path);
     FREEMEM(from_path);
     FREEMEM(to_path);
     return retval;
@@ -194,27 +199,25 @@ FSFolder_Rename_IMP(FSFolder *self, String* from, String 
*to) {
 bool
 FSFolder_Hard_Link_IMP(FSFolder *self, String *from,
                        String *to) {
-    char *from_path_ptr = S_fullpath_ptr(self, from);
-    char *to_path_ptr   = S_fullpath_ptr(self, to);
-    bool  retval        = S_hard_link(from_path_ptr, to_path_ptr);
-    FREEMEM(from_path_ptr);
-    FREEMEM(to_path_ptr);
+    // TODO: Update Folder entries.
+    char *from_path = S_fullpath_ptr(self, from);
+    char *to_path   = S_fullpath_ptr(self, to);
+    bool  retval    = S_hard_link(from_path, to_path);
+    FREEMEM(from_path);
+    FREEMEM(to_path);
     return retval;
 }
 
 bool
 FSFolder_Local_Delete_IMP(FSFolder *self, String *name) {
+    // TODO: Delete should only delete files. We should add RmDir for
+    // directories.
     FSFolderIVARS *const ivars = FSFolder_IVARS(self);
-
-    char *path_ptr = S_fullpath_ptr(self, name);
-#ifdef CHY_REMOVE_ZAPS_DIRS
-    bool result = !remove(path_ptr);
-#else
-    bool result = !rmdir(path_ptr) || !remove(path_ptr);
-#endif
+    char *path   = S_fullpath_ptr(self, name);
+    bool  retval = S_delete(path);
     DECREF(Hash_Delete(ivars->entries, name));
-    FREEMEM(path_ptr);
-    return result;
+    FREEMEM(path);
+    return retval;
 }
 
 void
@@ -393,18 +396,63 @@ S_absolutify(String *path) {
 }
 
 static bool
-S_hard_link(char *from8, char *to8) {
-    if (CreateHardLink(to8, from8, NULL)) {
+S_rename(const char *from_path, const char *to_path) {
+    // TODO: We should consider using MoveFileEx with
+    // MOVEFILE_REPLACE_EXISTING to better match POSIX semantics. But unlike
+    // POSIX, this allows to replace a file with a directory, breaking the
+    // tests.
+    if (MoveFileA(from_path, to_path) != 0) {
+        return true;
+    }
+    else {
+        ErrMsg_set_with_win_error("rename from '%s' to '%s' failed",
+                                  from_path, to_path);
+        return false;
+    }
+}
+
+static bool
+S_hard_link(const char *from_path, const char *to_path) {
+    if (CreateHardLinkA(to_path, from_path, NULL) != 0) {
         return true;
     }
     else {
         ErrMsg_set_with_win_error("CreateHardLink for new file '%s' "
                                   "from '%s' failed",
-                                  to8, from8);
+                                  to_path, from_path);
         return false;
     }
 }
 
+static bool
+S_delete(const char *path) {
+    if (RemoveDirectoryA(path) != 0) {
+        return true;
+    }
+    if (GetLastError() != ERROR_DIRECTORY) {
+        ErrMsg_set_with_win_error("removing '%s' failed", path);
+        return false;
+    }
+
+    // Open file with FILE_FLAG_DELETE_ON_CLOSE and close it immediately.
+    // In contrast to DeleteFile, this allows files opened with
+    // FILE_SHARE_DELETE to be eventually deleted.
+    DWORD share_mode = FILE_SHARE_READ
+                       | FILE_SHARE_WRITE
+                       | FILE_SHARE_DELETE;
+    DWORD flags      = FILE_FLAG_DELETE_ON_CLOSE;
+    DWORD attrs      = FILE_ATTRIBUTE_NORMAL;
+    HANDLE handle = CreateFileA(path, DELETE, share_mode, NULL, OPEN_EXISTING,
+                                flags | attrs, NULL);
+    if (handle == INVALID_HANDLE_VALUE) {
+        ErrMsg_set_with_win_error("removing '%s' failed", path);
+        return false;
+    }
+
+    CloseHandle(handle);
+    return true;
+}
+
 #elif (defined(CHY_HAS_UNISTD_H))
 
 static bool
@@ -432,10 +480,22 @@ S_absolutify(String *path) {
 }
 
 static bool
-S_hard_link(char *from8, char *to8) {
-    if (-1 == link(from8, to8)) {
+S_rename(const char *from_path, const char *to_path) {
+    if (rename(from_path, to_path) != 0) {
+        ErrMsg_set_with_errno("rename from '%s' to '%s' failed",
+                              from_path, to_path);
+        return false;
+    }
+    else {
+        return true;
+    }
+}
+
+static bool
+S_hard_link(const char *from_path, const char *to_path) {
+    if (link(from_path, to_path) != 0) {
         ErrMsg_set_with_errno("hard link for new file '%s' from '%s' failed",
-                              to8, from8);
+                              to_path, from_path);
         return false;
     }
     else {
@@ -443,6 +503,19 @@ S_hard_link(char *from8, char *to8) {
     }
 }
 
+static bool
+S_delete(const char *path) {
+#ifdef CHY_REMOVE_ZAPS_DIRS
+    bool result = !remove(path);
+#else
+    bool result = !rmdir(path) || !remove(path);
+#endif
+    if (!result) {
+        ErrMsg_set_with_errno("removing '%s' failed", path);
+    }
+    return result;
+}
+
 #else
   #error "Need either windows.h or unistd.h"
 #endif /* CHY_HAS_UNISTD_H vs. CHY_HAS_WINDOWS_H */

Reply via email to