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