瀏覽代碼

ADD: Rename with elevation (Unix)

Alexander Koblov 4 年之前
父節點
當前提交
20343b687c
共有 1 個文件被更改,包括 19 次插入83 次删除
  1. 19 83
      src/filesources/filesystem/ufilesystemsetfilepropertyoperation.pas

+ 19 - 83
src/filesources/filesystem/ufilesystemsetfilepropertyoperation.pas

@@ -322,8 +322,7 @@ var
 
 var
 {$IFDEF UNIX}
-  tmpFileName: String;
-  OldFileStat, NewFileStat: stat;
+  OldAttr, NewAttr: TFileAttributeData;
 {$ELSE}
   NewFileAttrs: TFileAttrs;
 {$ENDIF}
@@ -337,84 +336,28 @@ begin
     Exit(sfprSkipped);
 
 {$IFDEF UNIX}
-  if fpLstat(UTF8ToSys(OldName), OldFileStat) <> 0 then
-    Exit(sfprError);
-
   // Check if target file exists.
-  if fpLstat(UTF8ToSys(NewName), NewFileStat) = 0 then
+  if FileGetAttrUAC(NewName, NewAttr) then
   begin
+    // Special case when filenames differ only by case,
+    // see comments in mbRenameFile function for details
+    if (UTF8LowerCase(OldName) <> UTF8LowerCase(NewName)) then
+      OldAttr.FindData.st_ino:= not NewAttr.FindData.st_ino
+    else begin
+      if not FileGetAttrUAC(OldName, OldAttr) then
+        Exit(sfprError);
+    end;
     // Check if source and target are the same files (same inode and same device).
-    if (OldFileStat.st_ino = NewFileStat.st_ino) and
-       (OldFileStat.st_dev = NewFileStat.st_dev) then
+    if (OldAttr.FindData.st_ino = NewAttr.FindData.st_ino) and
+       (OldAttr.FindData.st_dev = NewAttr.FindData.st_dev) and
+       // Check number of links, if it is 1 then source and target names most
+       // probably differ only by case on a case-insensitive filesystem.
+       ((NewAttr.FindData.st_nlink = 1) {$IFNDEF DARWIN}or fpS_ISDIR(NewAttr.FindData.st_mode){$ENDIF}) then
     begin
-      // Check number of links.
-      // If it is 1 then source and target names most probably differ only
-      // by case on a case-insensitive filesystem. Direct rename() in such case
-      // fails on Linux, so we use a temporary file name and rename in two stages.
-      // If number of links is more than 1 then it's enough to simply unlink
-      // the source file, since both files are technically identical.
-      // (On Linux rename() returns success but doesn't do anything
-      // if renaming a file to its hard link.)
-      // We cannot use st_nlink for directories because it means "number of
-      // subdirectories"; hard links to directories are not supported on Linux
-      // or Windows anyway (on MacOSX they are). Therefore we always treat
-      // directories as if they were a single link and rename them using temporary name.
-
-      if (NewFileStat.st_nlink = 1) or BaseUnix.fpS_ISDIR(NewFileStat.st_mode) then
-      begin
-        tmpFileName := GetTempName(OldName);
-
-        if FpRename(UTF8ToSys(OldName), UTF8ToSys(tmpFileName)) = 0 then
-        begin
-          if fpLstat(UTF8ToSys(NewName), NewFileStat) = 0 then
-          begin
-            // We have renamed the old file but the new file name still exists,
-            // so this wasn't a single file on a case-insensitive filesystem
-            // accessible by two names that differ by case.
-
-            FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(OldName));  // Restore old file.
-{$IFDEF DARWIN}
-            // If it was a directory with multiple hard links then fall through
-            // to asking for overwrite and unlinking source link.
-            if not (BaseUnix.fpS_ISDIR(NewFileStat.st_mode) and (NewFileStat.st_nlink > 1)) then
-{$ENDIF}
-            Exit(sfprError);
-          end
-          else if FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(NewName)) = 0 then
-          begin
-            Exit(sfprSuccess);
-          end
-          else
-          begin
-            FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(OldName));  // Restore old file.
-            Exit(sfprError);
-          end;
-        end
-        else
-          Exit(sfprError);
-      end;
-
-      // Both names are hard links to the same file.
-
-      case AskIfOverwrite(NewFileStat.st_mode) of
-        fsourOverwrite: ; // continue
-        fsourSkip:
-          Exit(sfprSkipped);
-        fsourAbort:
-          RaiseAbortOperation;
-      end;
-
-      // Multiple links - simply unlink the source file.
-      if fpUnLink(UTF8ToSys(OldName)) = 0 then
-        Result := sfprSuccess
-      else
-        Result := sfprError;
-
-      Exit;
+      // File names differ only by case on a case-insensitive filesystem.
     end
-    else
-    begin
-      case AskIfOverwrite(NewFileStat.st_mode) of
+    else begin
+      case AskIfOverwrite(NewAttr.FindData.st_mode) of
         fsourOverwrite: ; // continue
         fsourSkip:
           Exit(sfprSkipped);
@@ -423,14 +366,7 @@ begin
       end;
     end;
   end;
-
-  if FpRename(UTF8ToSys(OldName), UTF8ToSys(NewName)) = 0 then
-    Result := sfprSuccess
-  else
-    Result := sfprError;
-
 {$ELSE}
-
   // Windows XP doesn't allow two filenames that differ only by case (even on NTFS).
   if UTF8LowerCase(OldName) <> UTF8LowerCase(NewName) then
   begin
@@ -446,12 +382,12 @@ begin
       end;
     end;
   end;
+{$ENDIF}
 
   if RenameFileUAC(OldName, NewName) then
     Result := sfprSuccess
   else
     Result := sfprError;
-{$ENDIF}
 end;
 
 end.