Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using Microsoft.Win32;

namespace Microsoft.Win32.SafeHandles
{
Expand Down
6 changes: 0 additions & 6 deletions src/System.IO.FileSystem/src/System/IO/Directory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.IO.Enumeration;
using System.Linq;
using System.Security;

namespace System.IO
{
Expand Down Expand Up @@ -42,9 +41,6 @@ public static DirectoryInfo CreateDirectory(string path)
}

// Tests if the given path refers to an existing DirectoryInfo on disk.
//
// Your application must have Read permission to the directory's
// contents.
public static bool Exists(string path)
{
try
Expand All @@ -59,8 +55,6 @@ public static bool Exists(string path)
return FileSystem.DirectoryExists(fullPath);
}
catch (ArgumentException) { }
catch (NotSupportedException) { } // Security can throw this on ":"
catch (SecurityException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }

Expand Down
100 changes: 18 additions & 82 deletions src/System.IO.FileSystem/src/System/IO/File.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -44,39 +43,18 @@ public static StreamWriter AppendText(string path)
return new StreamWriter(path, append: true);
}


// Copies an existing file to a new file. An exception is raised if the
// destination file already exists. Use the
// Copy(string, string, boolean) method to allow
// overwriting an existing file.
//
// The caller must have certain FileIOPermissions. The caller must have
// Read permission to sourceFileName and Create
// and Write permissions to destFileName.
//
/// <summary>
/// Copies an existing file to a new file.
/// An exception is raised if the destination file already exists.
/// </summary>
public static void Copy(string sourceFileName, string destFileName)
{
if (sourceFileName == null)
throw new ArgumentNullException(nameof(sourceFileName), SR.ArgumentNull_FileName);
if (destFileName == null)
throw new ArgumentNullException(nameof(destFileName), SR.ArgumentNull_FileName);
if (sourceFileName.Length == 0)
throw new ArgumentException(SR.Argument_EmptyFileName, nameof(sourceFileName));
if (destFileName.Length == 0)
throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName));

InternalCopy(sourceFileName, destFileName, false);
}
=> Copy(sourceFileName, destFileName, overwrite: false);

// Copies an existing file to a new file. If overwrite is
// false, then an IOException is thrown if the destination file
// already exists. If overwrite is true, the file is
// overwritten.
//
// The caller must have certain FileIOPermissions. The caller must have
// Read permission to sourceFileName
// and Write permissions to destFileName.
//
/// <summary>
/// Copies an existing file to a new file.
/// If <paramref name="overwrite"/> is false, an exception will be
/// raised if the destination exists. Otherwise it will be overwritten.
/// </summary>
public static void Copy(string sourceFileName, string destFileName, bool overwrite)
{
if (sourceFileName == null)
Expand All @@ -88,36 +66,13 @@ public static void Copy(string sourceFileName, string destFileName, bool overwri
if (destFileName.Length == 0)
throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName));

InternalCopy(sourceFileName, destFileName, overwrite);
FileSystem.CopyFile(Path.GetFullPath(sourceFileName), Path.GetFullPath(destFileName), overwrite);
}

/// <devdoc>
/// Note: This returns the fully qualified name of the destination file.
/// </devdoc>
internal static string InternalCopy(string sourceFileName, string destFileName, bool overwrite)
{
Debug.Assert(sourceFileName != null);
Debug.Assert(destFileName != null);
Debug.Assert(sourceFileName.Length > 0);
Debug.Assert(destFileName.Length > 0);

string fullSourceFileName = Path.GetFullPath(sourceFileName);
string fullDestFileName = Path.GetFullPath(destFileName);

FileSystem.CopyFile(fullSourceFileName, fullDestFileName, overwrite);

return fullDestFileName;
}


// Creates a file in a particular path. If the file exists, it is replaced.
// The file is opened with ReadWrite access and cannot be opened by another
// application until it has been closed. An IOException is thrown if the
// directory specified doesn't exist.
//
// Your application must have Create, Read, and Write permissions to
// the file.
//
public static FileStream Create(string path)
{
return Create(path, DefaultBufferSize);
Expand All @@ -127,48 +82,30 @@ public static FileStream Create(string path)
// The file is opened with ReadWrite access and cannot be opened by another
// application until it has been closed. An IOException is thrown if the
// directory specified doesn't exist.
//
// Your application must have Create, Read, and Write permissions to
// the file.
//
public static FileStream Create(string path, int bufferSize)
{
return new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize);
}
=> new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize);

public static FileStream Create(string path, int bufferSize, FileOptions options)
{
return new FileStream(path, FileMode.Create, FileAccess.ReadWrite,
FileShare.None, bufferSize, options);
}
=> new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize, options);

// Deletes a file. The file specified by the designated path is deleted.
// If the file does not exist, Delete succeeds without throwing
// an exception.
//
// On NT, Delete will fail for a file that is open for normal I/O
// or a file that is memory mapped.
//
// Your application must have Delete permission to the target file.
//
// On Windows, Delete will fail for a file that is open for normal I/O
// or a file that is memory mapped.
public static void Delete(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));

string fullPath = Path.GetFullPath(path);

FileSystem.DeleteFile(fullPath);
FileSystem.DeleteFile(Path.GetFullPath(path));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always call GetFullPath immediately on paths passed in to IO API? This is presumably in case current directory changes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we always call GetFullPath(). The current directory thing would impact things, and in some cases we need to know that the path is normalized. Historically I'm sure it was more about CAS and the preemptive checks we made in GFP than anything else.

}


// Tests if a file exists. The result is true if the file
// Tests whether a file exists. The result is true if the file
// given by the specified path exists; otherwise, the result is
// false. Note that if path describes a directory,
// Exists will return true.
//
// Your application must have Read permission for the target directory.
//
public static bool Exists(string path)
{
try
Expand All @@ -179,6 +116,7 @@ public static bool Exists(string path)
return false;

path = Path.GetFullPath(path);

// After normalizing, check whether path ends in directory separator.
// Otherwise, FillAttributeInfo removes it and we may return a false positive.
// GetFullPath should never return null
Expand All @@ -191,8 +129,6 @@ public static bool Exists(string path)
return FileSystem.FileExists(path);
}
catch (ArgumentException) { }
catch (NotSupportedException) { } // Security can throw this on ":"
catch (SecurityException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }

Expand Down
38 changes: 20 additions & 18 deletions src/System.IO.FileSystem/src/System/IO/FileInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,23 +71,15 @@ public bool IsReadOnly
}

public StreamReader OpenText()
=> new StreamReader(FullPath, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);
=> new StreamReader(NormalizedPath, Encoding.UTF8, detectEncodingFromByteOrderMarks: true);

public StreamWriter CreateText()
=> new StreamWriter(FullPath, append: false);
=> new StreamWriter(NormalizedPath, append: false);

public StreamWriter AppendText()
=> new StreamWriter(FullPath, append: true);
=> new StreamWriter(NormalizedPath, append: true);

public FileInfo CopyTo(string destFileName)
{
if (destFileName == null)
throw new ArgumentNullException(nameof(destFileName), SR.ArgumentNull_FileName);
if (destFileName.Length == 0)
throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName));

return new FileInfo(File.InternalCopy(FullPath, destFileName, false), isNormalized: true);
}
public FileInfo CopyTo(string destFileName) => CopyTo(destFileName, overwrite: false);

public FileInfo CopyTo(string destFileName, bool overwrite)
{
Expand All @@ -96,10 +88,12 @@ public FileInfo CopyTo(string destFileName, bool overwrite)
if (destFileName.Length == 0)
throw new ArgumentException(SR.Argument_EmptyFileName, nameof(destFileName));

return new FileInfo(File.InternalCopy(FullPath, destFileName, overwrite), isNormalized: true);
string destinationPath = Path.GetFullPath(destFileName);
FileSystem.CopyFile(FullPath, destinationPath, overwrite);
return new FileInfo(destinationPath, isNormalized: true);
}

public FileStream Create() => File.Create(FullPath);
public FileStream Create() => File.Create(NormalizedPath);

public override void Delete() => FileSystem.DeleteFile(FullPath);

Expand All @@ -110,13 +104,13 @@ public FileStream Open(FileMode mode, FileAccess access)
=> Open(mode, access, FileShare.None);

public FileStream Open(FileMode mode, FileAccess access, FileShare share)
=> new FileStream(FullPath, mode, access, share);
=> new FileStream(NormalizedPath, mode, access, share);

public FileStream OpenRead()
=> new FileStream(FullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);
=> new FileStream(NormalizedPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false);

public FileStream OpenWrite()
=> new FileStream(FullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
=> new FileStream(NormalizedPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);

// Moves a given file to a new location and potentially a new file name.
// This method does work across volumes.
Expand Down Expand Up @@ -153,7 +147,15 @@ public FileInfo Replace(string destinationFileName, string destinationBackupFile

public FileInfo Replace(string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors)
{
File.Replace(FullPath, destinationFileName, destinationBackupFileName, ignoreMetadataErrors);
if (destinationFileName == null)
throw new ArgumentNullException(nameof(destinationFileName));

FileSystem.ReplaceFile(
FullPath,
Path.GetFullPath(destinationFileName),
destinationBackupFileName != null ? Path.GetFullPath(destinationBackupFileName) : null,
ignoreMetadataErrors);

return new FileInfo(destinationFileName);
}

Expand Down
3 changes: 3 additions & 0 deletions src/System.IO.FileSystem/src/System/IO/FileSystemInfo.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,8 @@ internal static void ThrowNotFound(string path)
bool directoryError = !Directory.Exists(Path.GetDirectoryName(PathInternal.TrimEndingDirectorySeparator(path)));
throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.ENOENT), path, directoryError);
}

// There is no special handling for Unix- see Windows code for the reason we do this
internal string NormalizedPath => FullPath;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,9 @@ public void Refresh()
// when someone actually accesses a property
_dataInitialized = FileSystem.FillAttributeInfo(FullPath, ref _data, returnErrorOnNotFound: false);
}

// If we're opened around a enumerated path that ends in a period or space we need to be able to
// act on the path normally (open streams/writers/etc.)
internal string NormalizedPath => PathInternal.EnsureExtendedPrefixIfNeeded(FullPath);
}
}
Loading