1 // 2 // Mono.Unix/UnixFileSystemInfo.cs 3 // 4 // Authors: 5 // Jonathan Pryor (jonpryor@vt.edu) 6 // 7 // (C) 2004-2006 Jonathan Pryor 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining 10 // a copy of this software and associated documentation files (the 11 // "Software"), to deal in the Software without restriction, including 12 // without limitation the rights to use, copy, modify, merge, publish, 13 // distribute, sublicense, and/or sell copies of the Software, and to 14 // permit persons to whom the Software is furnished to do so, subject to 15 // the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be 18 // included in all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 // 28 29 using System; 30 using System.IO; 31 using System.Text; 32 using Mono.Unix; 33 34 namespace Mono.Unix { 35 36 public abstract class UnixFileSystemInfo 37 { 38 private Native.Stat stat; 39 private string fullPath; 40 private string originalPath; 41 private bool valid = false; 42 43 internal const FileSpecialAttributes AllSpecialAttributes = 44 FileSpecialAttributes.SetUserId | FileSpecialAttributes.SetGroupId | 45 FileSpecialAttributes.Sticky; 46 internal const FileTypes AllFileTypes = 47 FileTypes.Directory | FileTypes.CharacterDevice | FileTypes.BlockDevice | 48 FileTypes.RegularFile | FileTypes.Fifo | FileTypes.SymbolicLink | 49 FileTypes.Socket; 50 UnixFileSystemInfo(string path)51 protected UnixFileSystemInfo (string path) 52 { 53 UnixPath.CheckPath (path); 54 this.originalPath = path; 55 this.fullPath = UnixPath.GetFullPath (path); 56 Refresh (true); 57 } 58 UnixFileSystemInfo(String path, Native.Stat stat)59 internal UnixFileSystemInfo (String path, Native.Stat stat) 60 { 61 this.originalPath = path; 62 this.fullPath = UnixPath.GetFullPath (path); 63 this.stat = stat; 64 this.valid = true; 65 } 66 67 protected string FullPath { 68 get {return fullPath;} 69 set { 70 if (fullPath != value) { 71 UnixPath.CheckPath (value); 72 valid = false; 73 fullPath = value; 74 } 75 } 76 } 77 78 protected string OriginalPath { 79 get {return originalPath;} 80 set {originalPath = value;} 81 } 82 AssertValid()83 private void AssertValid () 84 { 85 Refresh (false); 86 if (!valid) 87 throw new InvalidOperationException ("Path doesn't exist!"); 88 } 89 90 public virtual string FullName { 91 get {return FullPath;} 92 } 93 94 public abstract string Name {get;} 95 96 public bool Exists { 97 get { 98 Refresh (true); 99 return valid; 100 } 101 } 102 103 public long Device { 104 get {AssertValid (); return Convert.ToInt64 (stat.st_dev);} 105 } 106 107 public long Inode { 108 get {AssertValid (); return Convert.ToInt64 (stat.st_ino);} 109 } 110 111 [CLSCompliant (false)] 112 public Native.FilePermissions Protection { 113 get {AssertValid (); return (Native.FilePermissions) stat.st_mode;} 114 set { 115 int r = Native.Syscall.chmod (FullPath, value); 116 UnixMarshal.ThrowExceptionForLastErrorIf (r); 117 } 118 } 119 120 public FileTypes FileType { 121 get { 122 AssertValid (); 123 return (FileTypes) (stat.st_mode & Native.FilePermissions.S_IFMT); 124 } 125 // no set as chmod(2) won't accept changing the file type. 126 } 127 128 public FileAccessPermissions FileAccessPermissions { 129 get { 130 AssertValid (); 131 int perms = (int) stat.st_mode; 132 return (FileAccessPermissions) (perms & (int) FileAccessPermissions.AllPermissions); 133 } 134 set { 135 AssertValid (); 136 int perms = (int) stat.st_mode; 137 perms &= (int) ~FileAccessPermissions.AllPermissions; 138 perms |= (int) value; 139 Protection = (Native.FilePermissions) perms; 140 } 141 } 142 143 public FileSpecialAttributes FileSpecialAttributes { 144 get { 145 AssertValid (); 146 int attrs = (int) stat.st_mode; 147 return (FileSpecialAttributes) (attrs & (int) AllSpecialAttributes); 148 } 149 set { 150 AssertValid (); 151 int perms = (int) stat.st_mode; 152 perms &= (int) ~AllSpecialAttributes; 153 perms |= (int) value; 154 Protection = (Native.FilePermissions) perms; 155 } 156 } 157 158 public long LinkCount { 159 get {AssertValid (); return Convert.ToInt64 (stat.st_nlink);} 160 } 161 162 public UnixUserInfo OwnerUser { 163 get {AssertValid (); return new UnixUserInfo (stat.st_uid);} 164 } 165 166 public long OwnerUserId { 167 get {AssertValid (); return stat.st_uid;} 168 } 169 170 public UnixGroupInfo OwnerGroup { 171 get {AssertValid (); return new UnixGroupInfo (stat.st_gid);} 172 } 173 174 public long OwnerGroupId { 175 get {AssertValid (); return stat.st_gid;} 176 } 177 178 public long DeviceType { 179 get {AssertValid (); return Convert.ToInt64 (stat.st_rdev);} 180 } 181 182 public long Length { 183 get {AssertValid (); return (long) stat.st_size;} 184 } 185 186 public long BlockSize { 187 get {AssertValid (); return (long) stat.st_blksize;} 188 } 189 190 public long BlocksAllocated { 191 get {AssertValid (); return (long) stat.st_blocks;} 192 } 193 194 public DateTime LastAccessTime { 195 get {AssertValid (); return Native.NativeConvert.ToDateTime (stat.st_atime, stat.st_atime_nsec);} 196 } 197 198 public DateTime LastAccessTimeUtc { 199 get {return LastAccessTime.ToUniversalTime ();} 200 } 201 202 public DateTime LastWriteTime { 203 get {AssertValid (); return Native.NativeConvert.ToDateTime (stat.st_mtime, stat.st_mtime_nsec);} 204 } 205 206 public DateTime LastWriteTimeUtc { 207 get {return LastWriteTime.ToUniversalTime ();} 208 } 209 210 public DateTime LastStatusChangeTime { 211 get {AssertValid (); return Native.NativeConvert.ToDateTime (stat.st_ctime, stat.st_ctime_nsec);} 212 } 213 214 public DateTime LastStatusChangeTimeUtc { 215 get {return LastStatusChangeTime.ToUniversalTime ();} 216 } 217 218 public bool IsDirectory { 219 get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFDIR);} 220 } 221 222 public bool IsCharacterDevice { 223 get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFCHR);} 224 } 225 226 public bool IsBlockDevice { 227 get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFBLK);} 228 } 229 230 public bool IsRegularFile { 231 get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFREG);} 232 } 233 234 public bool IsFifo { 235 get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFIFO);} 236 } 237 238 public bool IsSymbolicLink { 239 get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFLNK);} 240 } 241 242 public bool IsSocket { 243 get {AssertValid (); return IsFileType (stat.st_mode, Native.FilePermissions.S_IFSOCK);} 244 } 245 246 public bool IsSetUser { 247 get {AssertValid (); return IsSet (stat.st_mode, Native.FilePermissions.S_ISUID);} 248 } 249 250 public bool IsSetGroup { 251 get {AssertValid (); return IsSet (stat.st_mode, Native.FilePermissions.S_ISGID);} 252 } 253 254 public bool IsSticky { 255 get {AssertValid (); return IsSet (stat.st_mode, Native.FilePermissions.S_ISVTX);} 256 } 257 IsFileType(Native.FilePermissions mode, Native.FilePermissions type)258 internal static bool IsFileType (Native.FilePermissions mode, Native.FilePermissions type) 259 { 260 return (mode & Native.FilePermissions.S_IFMT) == type; 261 } 262 IsSet(Native.FilePermissions mode, Native.FilePermissions type)263 internal static bool IsSet (Native.FilePermissions mode, Native.FilePermissions type) 264 { 265 return (mode & type) == type; 266 } 267 268 [CLSCompliant (false)] CanAccess(Native.AccessModes mode)269 public bool CanAccess (Native.AccessModes mode) 270 { 271 int r = Native.Syscall.access (FullPath, mode); 272 return r == 0; 273 } 274 CreateLink(string path)275 public UnixFileSystemInfo CreateLink (string path) 276 { 277 int r = Native.Syscall.link (FullName, path); 278 UnixMarshal.ThrowExceptionForLastErrorIf (r); 279 return GetFileSystemEntry (path); 280 } 281 CreateSymbolicLink(string path)282 public UnixSymbolicLinkInfo CreateSymbolicLink (string path) 283 { 284 int r = Native.Syscall.symlink (FullName, path); 285 UnixMarshal.ThrowExceptionForLastErrorIf (r); 286 return new UnixSymbolicLinkInfo (path); 287 } 288 Delete()289 public abstract void Delete (); 290 291 [CLSCompliant (false)] GetConfigurationValue(Native.PathconfName name)292 public long GetConfigurationValue (Native.PathconfName name) 293 { 294 long r = Native.Syscall.pathconf (FullPath, name); 295 if (r == -1 && Native.Stdlib.GetLastError() != (Native.Errno) 0) 296 UnixMarshal.ThrowExceptionForLastError (); 297 return r; 298 } 299 Refresh()300 public void Refresh () 301 { 302 Refresh (true); 303 } 304 Refresh(bool force)305 internal void Refresh (bool force) 306 { 307 if (valid && !force) 308 return; 309 valid = GetFileStatus (FullPath, out this.stat); 310 } 311 GetFileStatus(string path, out Native.Stat stat)312 protected virtual bool GetFileStatus (string path, out Native.Stat stat) 313 { 314 return Native.Syscall.stat (path, out stat) == 0; 315 } 316 SetLength(long length)317 public void SetLength (long length) 318 { 319 int r; 320 do { 321 r = Native.Syscall.truncate (FullPath, length); 322 } while (UnixMarshal.ShouldRetrySyscall (r)); 323 UnixMarshal.ThrowExceptionForLastErrorIf (r); 324 } 325 SetOwner(long owner, long group)326 public virtual void SetOwner (long owner, long group) 327 { 328 uint _owner = Convert.ToUInt32 (owner); 329 uint _group = Convert.ToUInt32 (group); 330 int r = Native.Syscall.chown (FullPath, _owner, _group); 331 UnixMarshal.ThrowExceptionForLastErrorIf (r); 332 } 333 SetOwner(string owner)334 public void SetOwner (string owner) 335 { 336 Native.Passwd pw = Native.Syscall.getpwnam (owner); 337 if (pw == null) 338 throw new ArgumentException (Locale.GetText ("invalid username"), "owner"); 339 uint uid = pw.pw_uid; 340 uint gid = pw.pw_gid; 341 SetOwner ((long) uid, (long) gid); 342 } 343 SetOwner(string owner, string group)344 public void SetOwner (string owner, string group) 345 { 346 long uid = -1; 347 if (owner != null) 348 uid = new UnixUserInfo (owner).UserId; 349 long gid = -1; 350 if (group != null) 351 gid = new UnixGroupInfo (group).GroupId; 352 353 SetOwner (uid, gid); 354 } 355 SetOwner(UnixUserInfo owner)356 public void SetOwner (UnixUserInfo owner) 357 { 358 long uid, gid; 359 uid = gid = -1; 360 if (owner != null) { 361 uid = owner.UserId; 362 gid = owner.GroupId; 363 } 364 SetOwner (uid, gid); 365 } 366 SetOwner(UnixUserInfo owner, UnixGroupInfo group)367 public void SetOwner (UnixUserInfo owner, UnixGroupInfo group) 368 { 369 long uid, gid; 370 uid = gid = -1; 371 if (owner != null) 372 uid = owner.UserId; 373 if (group != null) 374 gid = owner.GroupId; 375 SetOwner (uid, gid); 376 } 377 ToString()378 public override string ToString () 379 { 380 return FullPath; 381 } 382 ToStat()383 public Native.Stat ToStat () 384 { 385 AssertValid (); 386 return stat; 387 } 388 GetFileSystemEntry(string path)389 public static UnixFileSystemInfo GetFileSystemEntry (string path) 390 { 391 UnixFileSystemInfo info; 392 if (TryGetFileSystemEntry (path, out info)) 393 return info; 394 395 UnixMarshal.ThrowExceptionForLastError (); 396 397 // Throw DirectoryNotFoundException because lstat(2) probably failed 398 // because of ENOTDIR (e.g. "/path/to/file/wtf"), so 399 // DirectoryNotFoundException is what would have been thrown anyway. 400 throw new DirectoryNotFoundException ("UnixMarshal.ThrowExceptionForLastError didn't throw?!"); 401 } 402 TryGetFileSystemEntry(string path, out UnixFileSystemInfo entry)403 public static bool TryGetFileSystemEntry (string path, out UnixFileSystemInfo entry) 404 { 405 Native.Stat stat; 406 int r = Native.Syscall.lstat (path, out stat); 407 if (r == -1) { 408 if (Native.Stdlib.GetLastError() == Native.Errno.ENOENT) { 409 entry = new UnixFileInfo (path); 410 return true; 411 } 412 entry = null; 413 return false; 414 } 415 416 if (IsFileType (stat.st_mode, Native.FilePermissions.S_IFDIR)) 417 entry = new UnixDirectoryInfo (path, stat); 418 else if (IsFileType (stat.st_mode, Native.FilePermissions.S_IFLNK)) 419 entry = new UnixSymbolicLinkInfo (path, stat); 420 else 421 entry = new UnixFileInfo (path, stat); 422 423 return true; 424 } 425 } 426 } 427 428 // vim: noexpandtab 429