Merge pull request #1909 from esdrubal/reflection
[mono.git] / mcs / class / Mono.Posix / Mono.Unix / UnixFileSystemInfo.cs
index bf648ec03e6d0b1db808a866d770b9cbfe0cd559..149f79b84ed966bff1e74d466dd82a066acfd8e8 100644 (file)
@@ -4,7 +4,7 @@
 // Authors:
 //   Jonathan Pryor (jonpryor@vt.edu)
 //
-// (C) 2004 Jonathan Pryor
+// (C) 2004-2006 Jonathan Pryor
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -35,11 +35,19 @@ namespace Mono.Unix {
 
        public abstract class UnixFileSystemInfo
        {
-               private Stat stat;
+               private Native.Stat stat;
                private string fullPath;
                private string originalPath;
                private bool valid = false;
 
+               internal const FileSpecialAttributes AllSpecialAttributes = 
+                       FileSpecialAttributes.SetUserId | FileSpecialAttributes.SetGroupId |
+                       FileSpecialAttributes.Sticky;
+               internal const FileTypes AllFileTypes = 
+                       FileTypes.Directory | FileTypes.CharacterDevice | FileTypes.BlockDevice |
+                       FileTypes.RegularFile | FileTypes.Fifo | FileTypes.SymbolicLink | 
+                       FileTypes.Socket;
+
                protected UnixFileSystemInfo (string path)
                {
                        UnixPath.CheckPath (path);
@@ -48,7 +56,7 @@ namespace Mono.Unix {
                        Refresh (true);
                }
 
-               internal UnixFileSystemInfo (String path, Stat stat)
+               internal UnixFileSystemInfo (String path, Native.Stat stat)
                {
                        this.originalPath = path;
                        this.fullPath = UnixPath.GetFullPath (path);
@@ -58,7 +66,13 @@ namespace Mono.Unix {
 
                protected string FullPath {
                        get {return fullPath;}
-                       set {fullPath = value;}
+                       set {
+                               if (fullPath != value) {
+                                       UnixPath.CheckPath (value);
+                                       valid = false;
+                                       fullPath = value;
+                               }
+                       }
                }
 
                protected string OriginalPath {
@@ -81,47 +95,88 @@ namespace Mono.Unix {
 
                public bool Exists {
                        get {
-                               int r = Syscall.access (FullPath, AccessMode.F_OK);
-                               if (r == 0)
-                                       return true;
-                               return false;
+                               Refresh (true);
+                               return valid;
                        }
                }
 
-               public ulong Device {
-                       get {AssertValid (); return stat.st_dev;}
+               public long Device {
+                       get {AssertValid (); return Convert.ToInt64 (stat.st_dev);}
                }
 
-               public ulong Inode {
-                       get {AssertValid (); return stat.st_ino;}
+               public long Inode {
+                       get {AssertValid (); return Convert.ToInt64 (stat.st_ino);}
+               }
+
+               [CLSCompliant (false)]
+               public Native.FilePermissions Protection {
+                       get {AssertValid (); return (Native.FilePermissions) stat.st_mode;}
+                       set {
+                               int r = Native.Syscall.chmod (FullPath, value);
+                               UnixMarshal.ThrowExceptionForLastErrorIf (r);
+                       }
                }
 
-               public FilePermissions Mode {
-                       get {AssertValid (); return stat.st_mode;}
+               public FileTypes FileType {
+                       get {
+                               AssertValid ();
+                               return (FileTypes) (stat.st_mode & Native.FilePermissions.S_IFMT);
+                       }
+                       // no set as chmod(2) won't accept changing the file type.
+               }
+
+               public FileAccessPermissions FileAccessPermissions {
+                       get {
+                               AssertValid (); 
+                               int perms = (int) stat.st_mode;
+                               return (FileAccessPermissions) (perms & (int) FileAccessPermissions.AllPermissions);
+                       }
+                       set {
+                               AssertValid ();
+                               int perms = (int) stat.st_mode;
+                               perms &= (int) ~FileAccessPermissions.AllPermissions;
+                               perms |= (int) value;
+                               Protection = (Native.FilePermissions) perms;
+                       }
                }
 
-               public FilePermissions Permissions {
-                       get {AssertValid (); return stat.st_mode & ~FilePermissions.S_IFMT;}
+               public FileSpecialAttributes FileSpecialAttributes {
+                       get {
+                               AssertValid ();
+                               int attrs = (int) stat.st_mode;
+                               return (FileSpecialAttributes) (attrs & (int) AllSpecialAttributes);
+                       }
+                       set {
+                               AssertValid ();
+                               int perms = (int) stat.st_mode;
+                               perms &= (int) ~AllSpecialAttributes;
+                               perms |= (int) value;
+                               Protection = (Native.FilePermissions) perms;
+                       }
                }
 
-               public FilePermissions FileType {
-                       get {AssertValid (); return stat.st_mode & FilePermissions.S_IFMT;}
+               public long LinkCount {
+                       get {AssertValid (); return Convert.ToInt64 (stat.st_nlink);}
                }
 
-               public ulong LinkCount {
-                       get {AssertValid (); return (ulong) stat.st_nlink;}
+               public UnixUserInfo OwnerUser {
+                       get {AssertValid (); return new UnixUserInfo (stat.st_uid);}
                }
 
-               public uint OwnerUser {
+               public long OwnerUserId {
                        get {AssertValid (); return stat.st_uid;}
                }
 
-               public uint OwnerGroup {
+               public UnixGroupInfo OwnerGroup {
+                       get {AssertValid (); return new UnixGroupInfo (stat.st_gid);}
+               }
+
+               public long OwnerGroupId {
                        get {AssertValid (); return stat.st_gid;}
                }
 
-               public ulong DeviceType {
-                       get {AssertValid (); return stat.st_rdev;}
+               public long DeviceType {
+                       get {AssertValid (); return Convert.ToInt64 (stat.st_rdev);}
                }
 
                public long Length {
@@ -137,7 +192,7 @@ namespace Mono.Unix {
                }
 
                public DateTime LastAccessTime {
-                       get {AssertValid (); return UnixConvert.ToDateTime (stat.st_atime);}
+                       get {AssertValid (); return Native.NativeConvert.ToDateTime (stat.st_atime, stat.st_atime_nsec);}
                }
 
                public DateTime LastAccessTimeUtc {
@@ -145,7 +200,7 @@ namespace Mono.Unix {
                }
 
                public DateTime LastWriteTime {
-                       get {AssertValid (); return UnixConvert.ToDateTime (stat.st_mtime);}
+                       get {AssertValid (); return Native.NativeConvert.ToDateTime (stat.st_mtime, stat.st_mtime_nsec);}
                }
 
                public DateTime LastWriteTimeUtc {
@@ -153,7 +208,7 @@ namespace Mono.Unix {
                }
 
                public DateTime LastStatusChangeTime {
-                       get {AssertValid (); return UnixConvert.ToDateTime (stat.st_ctime);}
+                       get {AssertValid (); return Native.NativeConvert.ToDateTime (stat.st_ctime, stat.st_ctime_nsec);}
                }
 
                public DateTime LastStatusChangeTimeUtc {
@@ -161,77 +216,83 @@ namespace Mono.Unix {
                }
 
                public bool IsDirectory {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_IFDIR);}
+                       get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFDIR);}
                }
 
                public bool IsCharacterDevice {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_IFCHR);}
+                       get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFCHR);}
                }
 
                public bool IsBlockDevice {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_IFBLK);}
+                       get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFBLK);}
                }
 
-               public bool IsFile {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_IFREG);}
+               public bool IsRegularFile {
+                       get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFREG);}
                }
 
-               public bool IsFIFO {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_IFIFO);}
+               public bool IsFifo {
+                       get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFIFO);}
                }
 
                public bool IsSymbolicLink {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_IFLNK);}
+                       get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFLNK);}
                }
 
                public bool IsSocket {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_IFSOCK);}
+                       get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFSOCK);}
                }
 
                public bool IsSetUser {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_ISUID);}
+                       get {AssertValid (); return IsSet (stat.st_mode, Native.FilePermissions.S_ISUID);}
                }
 
                public bool IsSetGroup {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_ISGID);}
+                       get {AssertValid (); return IsSet (stat.st_mode, Native.FilePermissions.S_ISGID);}
                }
 
                public bool IsSticky {
-                       get {AssertValid (); return IsType (stat.st_mode, FilePermissions.S_ISVTX);}
+                       get {AssertValid (); return IsSet (stat.st_mode, Native.FilePermissions.S_ISVTX);}
                }
 
-               internal static bool IsType (FilePermissions mode, FilePermissions type)
+               internal static bool IsFileType (Native.FilePermissions mode, Native.FilePermissions type)
+               {
+                       return (mode & Native.FilePermissions.S_IFMT) == type;
+               }
+
+               internal static bool IsSet (Native.FilePermissions mode, Native.FilePermissions type)
                {
                        return (mode & type) == type;
                }
 
-               public bool CanAccess (AccessMode mode)
+               [CLSCompliant (false)]
+               public bool CanAccess (Native.AccessModes mode)
                {
-                       int r = Syscall.access (FullPath, mode);
+                       int r = Native.Syscall.access (FullPath, mode);
                        return r == 0;
                }
 
                public UnixFileSystemInfo CreateLink (string path)
                {
-                       int r = Syscall.link (FullName, path);
+                       int r = Native.Syscall.link (FullName, path);
                        UnixMarshal.ThrowExceptionForLastErrorIf (r);
-                       return Create (path);
+                       return GetFileSystemEntry (path);
                }
 
                public UnixSymbolicLinkInfo CreateSymbolicLink (string path)
                {
-                       int r = Syscall.symlink (FullName, path);
+                       int r = Native.Syscall.symlink (FullName, path);
                        UnixMarshal.ThrowExceptionForLastErrorIf (r);
                        return new UnixSymbolicLinkInfo (path);
                }
 
                public abstract void Delete ();
 
-               public long GetConfigurationValue (PathConf name)
+               [CLSCompliant (false)]
+               public long GetConfigurationValue (Native.PathconfName name)
                {
-                       Syscall.SetLastError ((Error) 0);
-                       long r = Syscall.pathconf (FullPath, name);
-                       if (r == -1 && Syscall.GetLastError() != (Error) 0)
+                       long r = Native.Syscall.pathconf (FullPath, name);
+                       if (r == -1 && Native.Stdlib.GetLastError() != (Native.Errno) 0)
                                UnixMarshal.ThrowExceptionForLastError ();
                        return r;
                }
@@ -245,46 +306,72 @@ namespace Mono.Unix {
                {
                        if (valid && !force)
                                return;
-                       int r = Syscall.lstat (FullPath, out this.stat);
-                       valid = r == 0;
+                       valid = GetFileStatus (FullPath, out this.stat);
+               }
+
+               protected virtual bool GetFileStatus (string path, out Native.Stat stat)
+               {
+                       return Native.Syscall.stat (path, out stat) == 0;
                }
 
                public void SetLength (long length)
                {
                        int r;
                        do {
-                               r = Syscall.truncate (FullPath, length);
+                               r = Native.Syscall.truncate (FullPath, length);
                        }       while (UnixMarshal.ShouldRetrySyscall (r));
                        UnixMarshal.ThrowExceptionForLastErrorIf (r);
                }
 
-               public void SetPermissions (FilePermissions perms)
-               {
-                       int r = Syscall.chmod (FullPath, perms);
-                       UnixMarshal.ThrowExceptionForLastErrorIf (r);
-               }
-
-               public virtual void SetOwner (uint owner, uint group)
+               public virtual void SetOwner (long owner, long group)
                {
-                       int r = Syscall.chown (FullPath, owner, group);
+                       uint _owner = Convert.ToUInt32 (owner);
+                       uint _group = Convert.ToUInt32 (group);
+                       int r = Native.Syscall.chown (FullPath, _owner, _group);
                        UnixMarshal.ThrowExceptionForLastErrorIf (r);
                }
 
                public void SetOwner (string owner)
                {
-                       Passwd pw = Syscall.getpwnam (owner);
+                       Native.Passwd pw = Native.Syscall.getpwnam (owner);
                        if (pw == null)
                                throw new ArgumentException (Locale.GetText ("invalid username"), "owner");
                        uint uid = pw.pw_uid;
                        uint gid = pw.pw_gid;
-                       SetOwner (uid, gid);
+                       SetOwner ((long) uid, (long) gid);
                }
 
                public void SetOwner (string owner, string group)
                {
-                       uint uid = UnixUser.GetUserId (owner);
-                       uint gid = UnixGroup.GetGroupId (group);
+                       long uid = -1;
+                       if (owner != null)
+                               uid = new UnixUserInfo (owner).UserId;
+                       long gid = -1;
+                       if (group != null)
+                               gid = new UnixGroupInfo (group).GroupId;
+
+                       SetOwner (uid, gid);
+               }
 
+               public void SetOwner (UnixUserInfo owner)
+               {
+                       long uid, gid;
+                       uid = gid = -1;
+                       if (owner != null) {
+                               uid = owner.UserId;
+                               gid = owner.GroupId;
+                       }
+                       SetOwner (uid, gid);
+               }
+
+               public void SetOwner (UnixUserInfo owner, UnixGroupInfo group)
+               {
+                       long uid, gid;
+                       uid = gid = -1;
+                       if (owner != null)
+                               uid = owner.UserId;
+                       if (group != null)
+                               gid = owner.GroupId;
                        SetOwner (uid, gid);
                }
 
@@ -293,17 +380,47 @@ namespace Mono.Unix {
                        return FullPath;
                }
 
-               internal static UnixFileSystemInfo Create (string path)
+               public Native.Stat ToStat ()
                {
-                       Stat stat;
-                       int r = Syscall.lstat (path, out stat);
-                       UnixMarshal.ThrowExceptionForLastErrorIf (r);
+                       AssertValid ();
+                       return stat;
+               }
+
+               public static UnixFileSystemInfo GetFileSystemEntry (string path)
+               {
+                       UnixFileSystemInfo info;
+                       if (TryGetFileSystemEntry (path, out info))
+                               return info;
+
+                       UnixMarshal.ThrowExceptionForLastError ();
+
+                       // Throw DirectoryNotFoundException because lstat(2) probably failed
+                       // because of ENOTDIR (e.g. "/path/to/file/wtf"), so
+                       // DirectoryNotFoundException is what would have been thrown anyway.
+                       throw new DirectoryNotFoundException ("UnixMarshal.ThrowExceptionForLastError didn't throw?!");
+               }
+
+               public static bool TryGetFileSystemEntry (string path, out UnixFileSystemInfo entry)
+               {
+                       Native.Stat stat;
+                       int r = Native.Syscall.lstat (path, out stat);
+                       if (r == -1) {
+                               if (Native.Stdlib.GetLastError() == Native.Errno.ENOENT) {
+                                       entry = new UnixFileInfo (path);
+                                       return true;
+                               }
+                               entry = null;
+                               return false;
+                       }
+
+                       if (IsFileType (stat.st_mode, Native.FilePermissions.S_IFDIR))
+                               entry = new UnixDirectoryInfo (path, stat);
+                       else if (IsFileType (stat.st_mode, Native.FilePermissions.S_IFLNK))
+                               entry = new UnixSymbolicLinkInfo (path, stat);
+                       else
+                               entry = new UnixFileInfo (path, stat);
 
-                       if (IsType (stat.st_mode, FilePermissions.S_IFDIR))
-                               return new UnixDirectoryInfo (path, stat);
-                       else if (IsType (stat.st_mode, FilePermissions.S_IFLNK))
-                               return new UnixSymbolicLinkInfo (path, stat);
-                       return new UnixFileInfo (path, stat);
+                       return true;
                }
        }
 }