From c05ab5c33336c3cbb7783b1a48fb1176748bf7e4 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Tue, 28 Dec 2004 19:54:39 +0000 Subject: * Mono.Posix.dll.sources: Add UnixDriveInfo and UnixPath. * CdeclFunctions.cs: Correct the comments for AMD64 * UnixDirectoryInfo.cs: override Name; add Parent & Root properties; Correct Path usage (s/Path/FullPath/g). * UnixDriveInfo.cs: Added. Based on .NET 2.0 System.IO.DriveInfo docs, provides statvfs(2) and getfsfile(3) information about a mounted volume. GetDrives() wraps getfsent(3), thus parsing /etc/fstab. * UnixFile.cs: Use UnixConver.ToOpenFlags, deleting the local version. * UnixFileInfo.cs: Use UnixConver.ToOpenFlags, deleting the local version; override Name; add DirectoryName and Directory properties; * UnixFileSystemInfo.cs: Make more .NET-like, using FullPath and OriginalPath protected members, abstract Name property; Add CreateSymbolicLink; Remove ReadLink (it's now UnixSymbolicLinkInfo.Contents); Use lstat(2) for Create(string), so we properly detect Symbolic Links. * UnixPath.cs: Added; Path manipulation utility functions. * UnixSymbolicLinkInfo.cs: - Seal the class; - override new abstract member Name; - rename ReadLink to ContentsPath (and Contents) properties (why "Contents"? Because readlink(2) says "readlink places the contents of the symbolic link in the buffer...") - Add CreateSymbolicLinkTo(), which creates a symlink to the specified "normal" file svn path=/trunk/mcs/; revision=38140 --- mcs/class/Mono.Posix/ChangeLog | 4 + mcs/class/Mono.Posix/Mono.Posix.dll.sources | 4 +- mcs/class/Mono.Posix/Mono.Unix/CdeclFunction.cs | 18 +- mcs/class/Mono.Posix/Mono.Unix/ChangeLog | 26 +++ .../Mono.Posix/Mono.Unix/UnixDirectoryInfo.cs | 37 +++- mcs/class/Mono.Posix/Mono.Unix/UnixDriveInfo.cs | 169 +++++++++++++++++ mcs/class/Mono.Posix/Mono.Unix/UnixFile.cs | 54 +----- mcs/class/Mono.Posix/Mono.Unix/UnixFileInfo.cs | 83 +++------ .../Mono.Posix/Mono.Unix/UnixFileSystemInfo.cs | 77 ++++---- mcs/class/Mono.Posix/Mono.Unix/UnixPath.cs | 204 +++++++++++++++++++++ .../Mono.Posix/Mono.Unix/UnixSymbolicLinkInfo.cs | 63 ++++++- 11 files changed, 576 insertions(+), 163 deletions(-) create mode 100644 mcs/class/Mono.Posix/Mono.Unix/UnixDriveInfo.cs create mode 100644 mcs/class/Mono.Posix/Mono.Unix/UnixPath.cs (limited to 'mcs/class/Mono.Posix') diff --git a/mcs/class/Mono.Posix/ChangeLog b/mcs/class/Mono.Posix/ChangeLog index dd5a6b95479..27eb5dc598a 100644 --- a/mcs/class/Mono.Posix/ChangeLog +++ b/mcs/class/Mono.Posix/ChangeLog @@ -1,3 +1,7 @@ +2004-12-28 Jonathan Pryor + + * Mono.Posix.dll.sources: Add UnixDriveInfo and UnixPath. + 2004-11-22 Raja R Harinath * Makefile (CLEAN_FILES): Clean up make-map.exe and the duplicated diff --git a/mcs/class/Mono.Posix/Mono.Posix.dll.sources b/mcs/class/Mono.Posix/Mono.Posix.dll.sources index a1cac12b721..17996c0a75a 100644 --- a/mcs/class/Mono.Posix/Mono.Posix.dll.sources +++ b/mcs/class/Mono.Posix/Mono.Posix.dll.sources @@ -10,7 +10,9 @@ ./Mono.Unix/UnixConvert.cs ./Mono.Unix/UnixDirectory.cs ./Mono.Unix/UnixDirectoryInfo.cs +./Mono.Unix/UnixDriveInfo.cs ./Mono.Unix/UnixEnvironment.cs +./Mono.Unix/UnixEndPoint.cs ./Mono.Unix/UnixFile.cs ./Mono.Unix/UnixFileInfo.cs ./Mono.Unix/UnixFileSystemInfo.cs @@ -18,12 +20,12 @@ ./Mono.Unix/UnixGroupInfo.cs ./Mono.Unix/UnixIOException.cs ./Mono.Unix/UnixMarshal.cs +./Mono.Unix/UnixPath.cs ./Mono.Unix/UnixProcess.cs ./Mono.Unix/UnixStream.cs ./Mono.Unix/UnixSymbolicLinkInfo.cs ./Mono.Unix/UnixUser.cs ./Mono.Unix/UnixUserInfo.cs -./Mono.Unix/UnixEndPoint.cs ./Mono.Posix/Catalog.cs ./Mono.Posix/PeerCred.cs ./Mono.Posix/Syscall.cs diff --git a/mcs/class/Mono.Posix/Mono.Unix/CdeclFunction.cs b/mcs/class/Mono.Posix/Mono.Unix/CdeclFunction.cs index 04c7227f5fd..39d2d3a28e7 100644 --- a/mcs/class/Mono.Posix/Mono.Unix/CdeclFunction.cs +++ b/mcs/class/Mono.Posix/Mono.Unix/CdeclFunction.cs @@ -46,7 +46,7 @@ namespace Mono.Unix { // // Then call the Invoke method with the appropriate number of arguments: // - // printf.Invoke (new object[]{"hello, %s\n", "world!"}); + // printf.Invoke (new object[]{"hello, %s\n", "world!"}); // // In the background a P/Invoke definition for the method with the // requested argument types will be generated and invoked, invoking the @@ -54,15 +54,19 @@ namespace Mono.Unix { // calls with the same argument list do not generate new code, speeding up // the call sequence. // - // Invoking Cdecl functions is not portable across all platforms. In - // particular, AMD64 requires that the caller set EAX to the number of - // floating point arguments passed in the SSE registers. This is only - // required for variable argument/stdarg functions; consequently, Mono's JIT - // doesn't set EAX properly which can lead to failures. See: + // Invoking Cdecl functions is not guaranteed to be portable across all + // platforms. For example, AMD64 requires that the caller set EAX to the + // number of floating point arguments passed in the SSE registers. This + // is only required for variable argument/cdecl functions; consequently, + // the overload technique used by this class wouldn't normally work. + // Mono's AMD64 JIT works around this by always setting EAX on P/Invoke + // invocations, allowing CdeclFunction to work properly, but it will not + // necessarily always work. See also: // // http://lwn.net/Articles/5201/?format=printable // - // Consequently, Cdecl functions should be avoided on most platforms. + // Due to potential portability issues, cdecl functions should be avoided + // on most platforms. // // This class is intended to be thread-safe. public sealed class CdeclFunction diff --git a/mcs/class/Mono.Posix/Mono.Unix/ChangeLog b/mcs/class/Mono.Posix/Mono.Unix/ChangeLog index c99b833f455..b56c783caf0 100644 --- a/mcs/class/Mono.Posix/Mono.Unix/ChangeLog +++ b/mcs/class/Mono.Posix/Mono.Unix/ChangeLog @@ -1,3 +1,29 @@ +2004-12-28 Jonathan Pryor + + * CdeclFunctions.cs: Correct the comments for AMD64 + * UnixDirectoryInfo.cs: override Name; add Parent & Root properties; + Correct Path usage (s/Path/FullPath/g). + * UnixDriveInfo.cs: Added. Based on .NET 2.0 System.IO.DriveInfo docs, + provides statvfs(2) and getfsfile(3) information about a mounted volume. + GetDrives() wraps getfsent(3), thus parsing /etc/fstab. + * UnixFile.cs: Use UnixConver.ToOpenFlags, deleting the local version. + * UnixFileInfo.cs: Use UnixConver.ToOpenFlags, deleting the local version; + override Name; add DirectoryName and Directory properties; + * UnixFileSystemInfo.cs: Make more .NET-like, using FullPath and + OriginalPath protected members, abstract Name property; Add + CreateSymbolicLink; Remove ReadLink (it's now + UnixSymbolicLinkInfo.Contents); Use lstat(2) for Create(string), so we + properly detect Symbolic Links. + * UnixPath.cs: Added; Path manipulation utility functions. + * UnixSymbolicLinkInfo.cs: + - Seal the class; + - override new abstract member Name; + - rename ReadLink to ContentsPath (and Contents) properties + (why "Contents"? Because readlink(2) says "readlink places the + contents of the symbolic link in the buffer...") + - Add CreateSymbolicLinkTo(), which creates a symlink to the specified + "normal" file + 2004-12-28 Jonathan Pryor * Stdlib.cs: Add syslog(3) to XPrintfFunctions; Add additional printf(3) diff --git a/mcs/class/Mono.Posix/Mono.Unix/UnixDirectoryInfo.cs b/mcs/class/Mono.Posix/Mono.Unix/UnixDirectoryInfo.cs index ae97037cdcb..29c533a42bd 100644 --- a/mcs/class/Mono.Posix/Mono.Unix/UnixDirectoryInfo.cs +++ b/mcs/class/Mono.Posix/Mono.Unix/UnixDirectoryInfo.cs @@ -35,7 +35,7 @@ using Mono.Unix; namespace Mono.Unix { - public class UnixDirectoryInfo : UnixFileSystemInfo + public sealed class UnixDirectoryInfo : UnixFileSystemInfo { public UnixDirectoryInfo (string path) : base (path) @@ -47,9 +47,36 @@ namespace Mono.Unix { { } + public override string Name { + get { + string r = UnixPath.GetFileName (FullPath); + if (r == null || r == "") + return FullPath; + return r; + } + } + + public UnixDirectoryInfo Parent { + get { + string dirname = UnixPath.GetDirectoryName (FullPath); + if (dirname == null) + return null; + return new UnixDirectoryInfo (dirname); + } + } + + public UnixDirectoryInfo Root { + get { + string root = UnixPath.GetPathRoot (FullPath); + if (root == null) + return null; + return new UnixDirectoryInfo (root); + } + } + public void Create (FilePermissions mode) { - int r = Syscall.mkdir (Path, mode); + int r = Syscall.mkdir (FullPath, mode); UnixMarshal.ThrowExceptionForLastErrorIf (r); base.Refresh (); } @@ -76,14 +103,14 @@ namespace Mono.Unix { e.Delete (); } } - int r = Syscall.rmdir (Path); + int r = Syscall.rmdir (FullPath); UnixMarshal.ThrowExceptionForLastErrorIf (r); base.Refresh (); } public Dirent[] GetEntries () { - IntPtr dirp = Syscall.opendir (Path); + IntPtr dirp = Syscall.opendir (FullPath); if (dirp == IntPtr.Zero) UnixMarshal.ThrowExceptionForLastError (); @@ -123,7 +150,7 @@ namespace Mono.Unix { public Dirent[] GetEntries (Regex regex) { - IntPtr dirp = Syscall.opendir (Path); + IntPtr dirp = Syscall.opendir (FullPath); if (dirp == IntPtr.Zero) UnixMarshal.ThrowExceptionForLastError (); diff --git a/mcs/class/Mono.Posix/Mono.Unix/UnixDriveInfo.cs b/mcs/class/Mono.Posix/Mono.Unix/UnixDriveInfo.cs new file mode 100644 index 00000000000..8eacd5cf879 --- /dev/null +++ b/mcs/class/Mono.Posix/Mono.Unix/UnixDriveInfo.cs @@ -0,0 +1,169 @@ +// +// Mono.Unix/UnixDriveInfo.cs +// +// Authors: +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2004 Jonathan Pryor +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.IO; +using Mono.Unix; + +namespace Mono.Unix { + + public enum UnixDriveType { + Unknown, + NoRootDirectory, + Removable, + Fixed, + Network, + CDRom, + Ram + } + + // All methods & properties can throw IOException + public sealed class UnixDriveInfo + { + private Statvfs stat; + private Fstab fstab; + + public UnixDriveInfo (string mountPoint) + { + if (mountPoint == null) + throw new ArgumentNullException ("mountPoint"); + fstab = Syscall.getfsfile (mountPoint); + if (fstab == null) + throw new ArgumentException ("mountPoint isn't valid: " + mountPoint); + // throws ArgumentException if driveName isn't valid + // though .NET also has a DriveNotFoundException class, so maybe that's + // more appropriate? + } + + public static UnixDriveInfo GetForSpecialFile (string specialFile) + { + if (specialFile == null) + throw new ArgumentNullException ("specialFile"); + Fstab f = Syscall.getfsspec (specialFile); + if (f == null) + throw new ArgumentException ("specialFile isn't valid: " + specialFile); + return new UnixDriveInfo (f); + } + + private UnixDriveInfo (Fstab fstab) + { + this.fstab = fstab; + } + + public long AvailableFreeSpace { + get {Refresh (); return (long) (stat.f_bavail * stat.f_bsize);} + } + + public string DriveFormat { + get {return fstab.fs_vfstype;} + } + + public UnixDriveType DriveType { + get {return UnixDriveType.Unknown;} + } + + // this throws no exceptions + public bool IsReady { + get {return Refresh (false);} + } + + public string Name { + get {return fstab.fs_file;} + } + + public UnixDirectoryInfo RootDirectory { + get {return new UnixDirectoryInfo (fstab.fs_file);} + } + + public long TotalFreeSpace { + get {Refresh (); return (long) (stat.f_bfree * stat.f_bsize);} + } + + public long TotalSize { + get {Refresh (); return (long) (stat.f_frsize * stat.f_blocks);} + } + + // also throws SecurityException if caller lacks perms + public string VolumeLabel { + get {return fstab.fs_spec;} + // set {} + } + + public ulong MaximumFilenameLength { + get {Refresh (); return stat.f_namemax;} + } + + public static UnixDriveInfo[] GetDrives () + { + // throws IOException, UnauthorizedAccessException (no permission) + Syscall.SetLastError ((Error) 0); + int r = Syscall.setfsent (); + if (r != 1) + throw new IOException ("Error calling setfsent(3)"); + ArrayList entries = new ArrayList (); + try { + Fstab fs; + while ((fs = Syscall.getfsent()) != null) { + // avoid virtual entries, such as "swap" + if (fs.fs_file.StartsWith ("/")) + entries.Add (new UnixDriveInfo (fs)); + } + } + finally { + Syscall.endfsent (); + } + return (UnixDriveInfo[]) entries.ToArray (typeof(UnixDriveInfo)); + } + + public override string ToString () + { + return VolumeLabel; + } + + private void Refresh () + { + Refresh (true); + } + + private bool Refresh (bool throwException) + { + int r = Syscall.statvfs (fstab.fs_file, out stat); + if (r == -1 && throwException) { + Error e = Syscall.GetLastError (); + throw new IOException (UnixMarshal.GetErrorDescription (e), + UnixMarshal.CreateExceptionForError (e)); + } + else if (r == -1) + return false; + return true; + } + } +} + +// vim: noexpandtab diff --git a/mcs/class/Mono.Posix/Mono.Unix/UnixFile.cs b/mcs/class/Mono.Posix/Mono.Unix/UnixFile.cs index f2d65af295f..0b1fdee4cf4 100644 --- a/mcs/class/Mono.Posix/Mono.Unix/UnixFile.cs +++ b/mcs/class/Mono.Posix/Mono.Unix/UnixFile.cs @@ -173,7 +173,7 @@ namespace Mono.Unix { public static UnixStream Open (string path, FileMode mode) { - OpenFlags flags = ToOpenFlags (mode, FileAccess.ReadWrite); + OpenFlags flags = UnixConvert.ToOpenFlags (mode, FileAccess.ReadWrite); int fd = Syscall.open (path, flags); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); @@ -182,7 +182,7 @@ namespace Mono.Unix { public static UnixStream Open (string path, FileMode mode, FileAccess access) { - OpenFlags flags = ToOpenFlags (mode, access); + OpenFlags flags = UnixConvert.ToOpenFlags (mode, access); int fd = Syscall.open (path, flags); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); @@ -191,7 +191,7 @@ namespace Mono.Unix { public static UnixStream Open (string path, FileMode mode, FileAccess access, FilePermissions perms) { - OpenFlags flags = ToOpenFlags (mode, access); + OpenFlags flags = UnixConvert.ToOpenFlags (mode, access); int fd = Syscall.open (path, flags, perms); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); @@ -256,54 +256,6 @@ namespace Mono.Unix { SetLinkOwner (path, uid, gid); } - public static OpenFlags ToOpenFlags (FileMode mode, FileAccess access) - { - OpenFlags flags = 0; - switch (mode) { - case FileMode.CreateNew: - flags = OpenFlags.O_CREAT | OpenFlags.O_EXCL; - break; - case FileMode.Create: - flags = OpenFlags.O_CREAT | OpenFlags.O_TRUNC; - break; - case FileMode.Open: - // do nothing - break; - case FileMode.OpenOrCreate: - flags = OpenFlags.O_CREAT; - break; - case FileMode.Truncate: - flags = OpenFlags.O_TRUNC; - break; - case FileMode.Append: - flags = OpenFlags.O_APPEND; - break; - default: - throw new ArgumentException (Locale.GetText ("Unsupported mode value"), "mode"); - } - - // Is O_LARGEFILE supported? - int _v; - if (UnixConvert.TryFromOpenFlags (OpenFlags.O_LARGEFILE, out _v)) - flags |= OpenFlags.O_LARGEFILE; - - switch (access) { - case FileAccess.Read: - flags |= OpenFlags.O_RDONLY; - break; - case FileAccess.Write: - flags |= OpenFlags.O_WRONLY; - break; - case FileAccess.ReadWrite: - flags |= OpenFlags.O_RDWR; - break; - default: - throw new ArgumentException (Locale.GetText ("Unsupported access value"), "access"); - } - - return flags; - } - public static void AdviseNormalAccess (int fd, long offset, long len) { int r = Syscall.posix_fadvise (fd, offset, len, diff --git a/mcs/class/Mono.Posix/Mono.Unix/UnixFileInfo.cs b/mcs/class/Mono.Posix/Mono.Unix/UnixFileInfo.cs index 09a1666ad92..5a12445768f 100644 --- a/mcs/class/Mono.Posix/Mono.Unix/UnixFileInfo.cs +++ b/mcs/class/Mono.Posix/Mono.Unix/UnixFileInfo.cs @@ -33,7 +33,7 @@ using Mono.Unix; namespace Mono.Unix { - public class UnixFileInfo : UnixFileSystemInfo + public sealed class UnixFileInfo : UnixFileSystemInfo { public UnixFileInfo (string path) : base (path) @@ -45,9 +45,21 @@ namespace Mono.Unix { { } + public override string Name { + get {return UnixPath.GetFileName (FullPath);} + } + + public string DirectoryName { + get {return UnixPath.GetDirectoryName (FullPath);} + } + + public UnixDirectoryInfo Directory { + get {return new UnixDirectoryInfo (DirectoryName);} + } + public override void Delete () { - int r = Syscall.unlink (Path); + int r = Syscall.unlink (FullPath); UnixMarshal.ThrowExceptionForLastErrorIf (r); base.Refresh (); } @@ -62,7 +74,7 @@ namespace Mono.Unix { public UnixStream Create (FilePermissions mode) { - int fd = Syscall.creat (Path, mode); + int fd = Syscall.creat (FullPath, mode); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); base.Refresh (); @@ -71,7 +83,7 @@ namespace Mono.Unix { public UnixStream Open (OpenFlags flags) { - int fd = Syscall.open (Path, flags); + int fd = Syscall.open (FullPath, flags); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); return new UnixStream (fd); @@ -79,7 +91,7 @@ namespace Mono.Unix { public UnixStream Open (OpenFlags flags, FilePermissions mode) { - int fd = Syscall.open (Path, flags, mode); + int fd = Syscall.open (FullPath, flags, mode); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); return new UnixStream (fd); @@ -87,8 +99,8 @@ namespace Mono.Unix { public UnixStream Open (FileMode mode) { - OpenFlags flags = ToOpenFlags (mode, FileAccess.ReadWrite); - int fd = Syscall.open (Path, flags); + OpenFlags flags = UnixConvert.ToOpenFlags (mode, FileAccess.ReadWrite); + int fd = Syscall.open (FullPath, flags); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); return new UnixStream (fd); @@ -96,8 +108,8 @@ namespace Mono.Unix { public UnixStream Open (FileMode mode, FileAccess access) { - OpenFlags flags = ToOpenFlags (mode, access); - int fd = Syscall.open (Path, flags); + OpenFlags flags = UnixConvert.ToOpenFlags (mode, access); + int fd = Syscall.open (FullPath, flags); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); return new UnixStream (fd); @@ -105,8 +117,8 @@ namespace Mono.Unix { public UnixStream Open (FileMode mode, FileAccess access, FilePermissions perms) { - OpenFlags flags = ToOpenFlags (mode, access); - int fd = Syscall.open (Path, flags, perms); + OpenFlags flags = UnixConvert.ToOpenFlags (mode, access); + int fd = Syscall.open (FullPath, flags, perms); if (fd < 0) UnixMarshal.ThrowExceptionForLastError (); return new UnixStream (fd); @@ -121,55 +133,6 @@ namespace Mono.Unix { { return Open (FileMode.OpenOrCreate, FileAccess.Write); } - - public static OpenFlags ToOpenFlags (FileMode mode, FileAccess access) - { - OpenFlags flags = 0; - switch (mode) { - case FileMode.CreateNew: - flags = OpenFlags.O_CREAT | OpenFlags.O_EXCL; - break; - case FileMode.Create: - flags = OpenFlags.O_CREAT | OpenFlags.O_TRUNC; - break; - case FileMode.Open: - // do nothing - break; - case FileMode.OpenOrCreate: - flags = OpenFlags.O_CREAT; - break; - case FileMode.Truncate: - flags = OpenFlags.O_TRUNC; - break; - case FileMode.Append: - flags = OpenFlags.O_APPEND; - break; - default: - throw new ArgumentOutOfRangeException ("mode", mode, - Locale.GetText ("Unsupported mode value")); - } - - int _ignored; - if (UnixConvert.TryFromOpenFlags (OpenFlags.O_LARGEFILE, out _ignored)) - flags |= OpenFlags.O_LARGEFILE; - - switch (access) { - case FileAccess.Read: - flags |= OpenFlags.O_RDONLY; - break; - case FileAccess.Write: - flags |= OpenFlags.O_WRONLY; - break; - case FileAccess.ReadWrite: - flags |= OpenFlags.O_RDWR; - break; - default: - throw new ArgumentOutOfRangeException ("access", access, - Locale.GetText ("Unsupported access value")); - } - - return flags; - } } } diff --git a/mcs/class/Mono.Posix/Mono.Unix/UnixFileSystemInfo.cs b/mcs/class/Mono.Posix/Mono.Unix/UnixFileSystemInfo.cs index 5b0b3ca1819..8b173c1bb46 100644 --- a/mcs/class/Mono.Posix/Mono.Unix/UnixFileSystemInfo.cs +++ b/mcs/class/Mono.Posix/Mono.Unix/UnixFileSystemInfo.cs @@ -36,25 +36,34 @@ namespace Mono.Unix { public abstract class UnixFileSystemInfo { private Stat stat; - private string path; + private string fullPath; + private string originalPath; private bool valid = false; protected UnixFileSystemInfo (string path) { - this.path = path; + UnixPath.CheckPath (path); + this.originalPath = path; + this.fullPath = UnixPath.GetFullPath (path); Refresh (true); } internal UnixFileSystemInfo (String path, Stat stat) { - this.path = path; + this.originalPath = path; + this.fullPath = UnixPath.GetFullPath (path); this.stat = stat; this.valid = true; } - protected string Path { - get {return path;} - set {path = value;} + protected string FullPath { + get {return fullPath;} + set {fullPath = value;} + } + + protected string OriginalPath { + get {return originalPath;} + set {originalPath = value;} } private void AssertValid () @@ -64,9 +73,15 @@ namespace Mono.Unix { throw new InvalidOperationException ("Path doesn't exist!"); } + public virtual string FullName { + get {return FullPath;} + } + + public abstract string Name {get;} + public bool Exists { get { - int r = Syscall.access (path, AccessMode.F_OK); + int r = Syscall.access (FullPath, AccessMode.F_OK); if (r == 0) return true; return false; @@ -192,42 +207,29 @@ namespace Mono.Unix { public bool CanAccess (AccessMode mode) { - int r = Syscall.access (path, mode); + int r = Syscall.access (FullPath, mode); return r == 0; } + public UnixSymbolicLinkInfo CreateSymbolicLink (string path) + { + int r = Syscall.symlink (FullName, path); + UnixMarshal.ThrowExceptionForLastErrorIf (r); + return new UnixSymbolicLinkInfo (path); + } + public abstract void Delete (); public long GetConfigurationValue (PathConf name) { Syscall.SetLastError ((Error) 0); - long r = Syscall.pathconf (Path, name); + long r = Syscall.pathconf (FullPath, name); if (r == -1 && Syscall.GetLastError() != (Error) 0) UnixMarshal.ThrowExceptionForLastError (); return r; } - // TODO: Should ReadLink be in UnixSymbolicLinkInfo? - public string ReadLink () - { - string r = TryReadLink (); - if (r == null) - UnixMarshal.ThrowExceptionForLastError (); - return r; - } - - public string TryReadLink () - { - // Who came up with readlink(2)? There doesn't seem to be a way to - // properly handle it. - StringBuilder sb = new StringBuilder (512); - int r = Syscall.readlink (path, sb); - if (r == -1) - return null; - return sb.ToString().Substring (0, r); - } - - public new void Refresh () + public void Refresh () { Refresh (true); } @@ -236,7 +238,7 @@ namespace Mono.Unix { { if (valid && !force) return; - int r = Syscall.stat (path, out this.stat); + int r = Syscall.lstat (FullPath, out this.stat); valid = r == 0; } @@ -244,20 +246,20 @@ namespace Mono.Unix { { int r; do { - r = Syscall.truncate (path, length); + r = Syscall.truncate (FullPath, length); } while (UnixMarshal.ShouldRetrySyscall (r)); UnixMarshal.ThrowExceptionForLastErrorIf (r); } public void SetPermissions (FilePermissions perms) { - int r = Syscall.chmod (path, perms); + int r = Syscall.chmod (FullPath, perms); UnixMarshal.ThrowExceptionForLastErrorIf (r); } public virtual void SetOwner (uint owner, uint group) { - int r = Syscall.chown (path, owner, group); + int r = Syscall.chown (FullPath, owner, group); UnixMarshal.ThrowExceptionForLastErrorIf (r); } @@ -281,12 +283,15 @@ namespace Mono.Unix { public override string ToString () { - return path; + return FullPath; } internal static UnixFileSystemInfo Create (string path) { - Stat stat = UnixFile.GetFileStatus (path); + Stat stat; + int r = Syscall.lstat (path, out stat); + UnixMarshal.ThrowExceptionForLastErrorIf (r); + if (IsType (stat.st_mode, FilePermissions.S_IFDIR)) return new UnixDirectoryInfo (path, stat); else if (IsType (stat.st_mode, FilePermissions.S_IFLNK)) diff --git a/mcs/class/Mono.Posix/Mono.Unix/UnixPath.cs b/mcs/class/Mono.Posix/Mono.Unix/UnixPath.cs new file mode 100644 index 00000000000..679c2c1405a --- /dev/null +++ b/mcs/class/Mono.Posix/Mono.Unix/UnixPath.cs @@ -0,0 +1,204 @@ +// +// Mono.Unix/UnixPath.cs +// +// Authors: +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2004 Jonathan Pryor +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Text; +using Mono.Unix; + +namespace Mono.Unix { + + public sealed class UnixPath + { + private UnixPath () {} + + public static readonly char DirectorySeparatorChar = '/'; + public static readonly char AltDirectorySeparatorChar = '/'; + public static readonly char[] InvalidPathChars = new char[]{'\0'}; + public static readonly char PathSeparator = ':'; + public static readonly char VolumeSeparatorChar = '/'; + + public static string Combine (string path1, params string[] paths) + { + if (path1 == null) + throw new ArgumentNullException ("path1"); + if (paths == null) + throw new ArgumentNullException ("paths"); + if (path1.IndexOfAny (InvalidPathChars) != -1) + throw new ArgumentException ("Illegal characters in path", "path1"); + + int len = path1.Length + 1; + for (int i = 0; i < paths.Length; ++i) { + if (paths [i] == null) + throw new ArgumentNullException ("paths"); + len += paths [i].Length + 1; + } + + StringBuilder sb = new StringBuilder (len); + sb.Append (path1); + for (int i = 0; i < paths.Length; ++i) + Combine (sb, paths [i]); + return sb.ToString (); + } + + private static void Combine (StringBuilder path, string part) + { + if (part.IndexOfAny (InvalidPathChars) != -1) + throw new ArgumentException ("Illegal characters in path", "path1"); + char end = path [path.Length-1]; + if (end != DirectorySeparatorChar && + end != AltDirectorySeparatorChar && + end != VolumeSeparatorChar) + path.Append (DirectorySeparatorChar); + path.Append (part); + } + + public static string GetDirectoryName (string path) + { + CheckPath (path); + + int lastDir = path.LastIndexOf (DirectorySeparatorChar); + if (lastDir > 0) + return path.Substring (0, lastDir); + return ""; + } + + public static string GetFileName (string path) + { + if (path == null || path.Length == 0) + return path; + + int lastDir = path.LastIndexOf (DirectorySeparatorChar); + if (lastDir >= 0) + return path.Substring (lastDir+1); + + return path; + } + + public static string GetFullPath (string path) + { + path = _GetFullPath (path); + string [] dirs; + int lastIndex; + GetPathComponents (path, out dirs, out lastIndex); + return string.Join ("/", dirs, 0, lastIndex); + } + + private static string _GetFullPath (string path) + { + if (path == null) + throw new ArgumentNullException ("path"); + if (!IsPathRooted (path)) + path = UnixDirectory.GetCurrentDirectory() + DirectorySeparatorChar + path; + + return path; + } + + private static void GetPathComponents (string path, + out string[] components, out int lastIndex) + { + string [] dirs = path.Split (DirectorySeparatorChar); + int target = 0; + for (int i = 0; i < dirs.Length; ++i) { + if (dirs [i] == "." || (i != 0 && dirs [i] == string.Empty)) continue; + else if (dirs [i] == "..") { + if (target != 0) --target; + } + else + dirs [target++] = dirs [i]; + } + components = dirs; + lastIndex = target; + } + + public static string GetPathRoot (string path) + { + if (path == null) + return null; + if (!IsPathRooted (path)) + return ""; + return "/"; + } + + public static string GetRealPath (string path) + { + path = _GetFullPath (path); + string [] dirs; + int lastIndex; + GetPathComponents (path, out dirs, out lastIndex); + StringBuilder realPath = new StringBuilder (); + for (int i = 0; i < dirs.Length; ++i) { + realPath.Append ("/").Append (dirs [i]); + string p = _GetRealPath (realPath.ToString()); + realPath.Remove (0, realPath.Length); + realPath.Append (p); + } + return realPath.ToString (); + } + + private static string _GetRealPath (string path) + { + StringBuilder buf = new StringBuilder (1024); + int r; + do { + r = Syscall.readlink (path, buf); + if (r == -1) { + Error e; + switch (e = Syscall.GetLastError()) { + case Error.EINVAL: + // path isn't a symbolic link + return path; + default: + UnixMarshal.ThrowExceptionForError (e); + break; + } + } + path = buf.ToString (); + } while (r == 0); + + return path; + } + + public static bool IsPathRooted (string path) + { + if (path == null || path.Length == 0) + return false; + return path [0] == DirectorySeparatorChar; + } + + internal static void CheckPath (string path) + { + if (path == null) + throw new ArgumentNullException (); + if (path.IndexOfAny (UnixPath.InvalidPathChars) != -1) + throw new ArgumentException ("Invalid characters in path."); + } + } +} + +// vim: noexpandtab diff --git a/mcs/class/Mono.Posix/Mono.Unix/UnixSymbolicLinkInfo.cs b/mcs/class/Mono.Posix/Mono.Unix/UnixSymbolicLinkInfo.cs index 322f7069f4c..ea0336da154 100644 --- a/mcs/class/Mono.Posix/Mono.Unix/UnixSymbolicLinkInfo.cs +++ b/mcs/class/Mono.Posix/Mono.Unix/UnixSymbolicLinkInfo.cs @@ -33,7 +33,7 @@ using Mono.Unix; namespace Mono.Unix { - public class UnixSymbolicLinkInfo : UnixFileSystemInfo + public sealed class UnixSymbolicLinkInfo : UnixFileSystemInfo { public UnixSymbolicLinkInfo (string path) : base (path) @@ -45,18 +45,75 @@ namespace Mono.Unix { { } + public override string Name { + get {return UnixPath.GetFileName (FullPath);} + } + + // maximum number of bytes read from the symbolic link + public static readonly int ContentsLength = 1024; + + public UnixFileSystemInfo Contents { + get { + return UnixFileSystemInfo.Create (ContentsPath); + } + } + + public string ContentsPath { + get { + return ReadLink (); + } + } + + public bool HasContents { + get { + return TryReadLink () != null; + } + } + + public void CreateSymbolicLinkTo (string path) + { + int r = Syscall.symlink (path, OriginalPath); + UnixMarshal.ThrowExceptionForLastErrorIf (r); + } + + public void CreateSymbolicLinkTo (UnixFileSystemInfo path) + { + int r = Syscall.symlink (path.FullName, OriginalPath); + UnixMarshal.ThrowExceptionForLastErrorIf (r); + } + public override void Delete () { - int r = Syscall.unlink (Path); + int r = Syscall.unlink (FullPath); UnixMarshal.ThrowExceptionForLastErrorIf (r); base.Refresh (); } public override void SetOwner (uint owner, uint group) { - int r = Syscall.lchown (Path, owner, group); + int r = Syscall.lchown (FullPath, owner, group); UnixMarshal.ThrowExceptionForLastErrorIf (r); } + + // TODO: Should ReadLink be in UnixSymbolicLinkInfo? + private string ReadLink () + { + string r = TryReadLink (); + if (r == null) + UnixMarshal.ThrowExceptionForLastError (); + return r; + } + + private string TryReadLink () + { + // Who came up with readlink(2)? There doesn't seem to be a way to + // properly handle it. + StringBuilder sb = new StringBuilder (ContentsLength+1); + int r = Syscall.readlink (FullPath, sb, (ulong) ContentsLength); + if (r == -1) + return null; + return sb.ToString().Substring (0, r); + } } } -- cgit v1.2.3