Merge pull request #1349 from martinjt/MachineKeyProtect
[mono.git] / mcs / class / Mono.Posix / Mono.Unix / UnixFileSystemInfo.cs
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
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
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
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
258                 internal static bool IsFileType (Native.FilePermissions mode, Native.FilePermissions type)
259                 {
260                         return (mode & Native.FilePermissions.S_IFMT) == type;
261                 }
262
263                 internal static bool IsSet (Native.FilePermissions mode, Native.FilePermissions type)
264                 {
265                         return (mode & type) == type;
266                 }
267
268                 [CLSCompliant (false)]
269                 public bool CanAccess (Native.AccessModes mode)
270                 {
271                         int r = Native.Syscall.access (FullPath, mode);
272                         return r == 0;
273                 }
274
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
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
289                 public abstract void Delete ();
290
291                 [CLSCompliant (false)]
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
300                 public void Refresh ()
301                 {
302                         Refresh (true);
303                 }
304
305                 internal void Refresh (bool force)
306                 {
307                         if (valid && !force)
308                                 return;
309                         valid = GetFileStatus (FullPath, out this.stat);
310                 }
311
312                 protected virtual bool GetFileStatus (string path, out Native.Stat stat)
313                 {
314                         return Native.Syscall.stat (path, out stat) == 0;
315                 }
316
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
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
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
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
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
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
378                 public override string ToString ()
379                 {
380                         return FullPath;
381                 }
382
383                 public Native.Stat ToStat ()
384                 {
385                         AssertValid ();
386                         return stat;
387                 }
388
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
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