source: branches/eraser6/6.0/Eraser.Util/StreamInfo.cs @ 2280

Revision 2280, 11.9 KB checked in by lowjoel, 4 years ago (diff)

Don't allow the UnauthorizedAccessException? to propagate up the stack; this is a problem which getting the length of the file may trigger, and for such situations, return 0.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25
26using Microsoft.Win32.SafeHandles;
27using System.IO;
28using System.ComponentModel;
29using System.Runtime.InteropServices;
30
31namespace Eraser.Util
32{
33    public class StreamInfo
34    {
35        /// <summary>
36        /// Initializes a new instance of the Eraser.Util.StreamInfo class, which
37        /// acts as a wrapper for a file path.
38        /// </summary>
39        /// <param name="path">The fully qualified name (with :ADSName for ADSes)
40        /// of the new file, or the relative file name.</param>
41        public StreamInfo(string path)
42        {
43            //Separate the path into the ADS and the file.
44            if (path.IndexOf(':') != path.LastIndexOf(':'))
45            {
46                int streamNameColon = path.IndexOf(':', path.IndexOf(':') + 1);
47                fileName = path.Substring(0, streamNameColon);
48                streamName = path.Substring(streamNameColon + 1);
49            }
50            else
51            {
52                fileName = path;
53            }
54        }
55
56        /// <summary>
57        /// Gets an instance of the parent directory.
58        /// </summary>
59        public DirectoryInfo Directory
60        {
61            get
62            {
63                return new DirectoryInfo(DirectoryName);
64            }
65        }
66
67        /// <summary>
68        /// Gets a string representing the containing directory's full path.
69        /// </summary>
70        public string DirectoryName
71        {
72            get
73            {
74                return fileName.Substring(0, fileName.LastIndexOf(Path.DirectorySeparatorChar) + 1);
75            }
76        }
77
78        /// <summary>
79        /// Gets the full name of the file, including the stream name.
80        /// </summary>
81        public string FullName
82        {
83            get
84            {
85                if (streamName != null)
86                    return fileName + ':' + streamName;
87                return fileName;
88            }
89        }
90
91        /// <summary>
92        /// Gets the name of the file.
93        /// </summary>
94        public string Name
95        {
96            get { return fileName; }
97        }
98
99        /// <summary>
100        /// Gets an instance of the main file. If this object refers to an ADS, the
101        /// result is null.
102        /// </summary>
103        public FileInfo File
104        {
105            get
106            {
107                if (streamName == null)
108                    return new FileInfo(fileName);
109                return null;
110            }
111        }
112
113        /// <summary>
114        /// Gets or sets the file attributes on this stream.
115        /// </summary>
116        public FileAttributes Attributes
117        {
118            get { return (FileAttributes)KernelApi.NativeMethods.GetFileAttributes(FullName); }
119            set { KernelApi.NativeMethods.SetFileAttributes(FullName, (uint)value); }
120        }
121       
122        /// <summary>
123        /// Gets a value indicating whether the stream exists.
124        /// </summary>
125        public bool Exists
126        {
127            get
128            {
129                using (SafeFileHandle handle = KernelApi.NativeMethods.CreateFile(
130                    FullName, KernelApi.NativeMethods.GENERIC_READ,
131                    KernelApi.NativeMethods.FILE_SHARE_READ, IntPtr.Zero,
132                    KernelApi.NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero))
133                {
134                    if (!handle.IsInvalid)
135                        return true;
136                    else
137                        switch (Marshal.GetLastWin32Error())
138                        {
139                            case 2: /*ERROR_FILE_NOT_FOUND*/
140                            case 3: /*ERROR_PATH_NOT_FOUND*/
141                                return false;
142
143                            case 32: /*ERROR_SHARING_VIOLATION*/
144                                return true;
145                        }
146   
147                    throw KernelApi.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
148                }
149            }
150        }
151
152        /// <summary>
153        /// Gets or sets a value that determines if the current file is read only.
154        /// </summary>
155        public bool IsReadOnly
156        {
157            get
158            {
159                return (Attributes & FileAttributes.ReadOnly) != 0;
160            }
161
162            set
163            {
164                if (value)
165                    Attributes |= FileAttributes.ReadOnly;
166                else
167                    Attributes &= ~FileAttributes.ReadOnly;
168            }
169        }
170
171        /// <summary>
172        /// Gets the size of the current stream.
173        /// </summary>
174        public long Length
175        {
176            get
177            {
178                try
179                {
180                    long fileSize;
181                    using (SafeFileHandle handle = fileHandle)
182                        if (KernelApi.NativeMethods.GetFileSizeEx(handle, out fileSize))
183                            return fileSize;
184                }
185                catch (UnauthorizedAccessException)
186                {
187                    //Swallow: we just return 0 in this situation as there's nothing we can
188                    //do about this error.
189                }
190
191                return 0;
192            }
193        }
194
195        public DateTime LastAccessTime
196        {
197            get
198            {
199                DateTime creationTime, lastAccess, lastWrite;
200                GetFileTime(out creationTime, out lastAccess, out lastWrite);
201                return lastAccess;
202            }
203            set
204            {
205                SetFileTime(DateTime.MinValue, value, DateTime.MinValue);
206            }
207        }
208
209        public DateTime LastWriteTime
210        {
211            get
212            {
213                DateTime creationTime, lastAccess, lastWrite;
214                GetFileTime(out creationTime, out lastAccess, out lastWrite);
215                return lastWrite;
216            }
217            set
218            {
219                SetFileTime(DateTime.MinValue, DateTime.MinValue, value);
220            }
221        }
222
223        public DateTime CreationTime
224        {
225            get
226            {
227                DateTime creationTime, lastAccess, lastWrite;
228                GetFileTime(out creationTime, out lastAccess, out lastWrite);
229                return creationTime;
230            }
231            set
232            {
233                SetFileTime(value, DateTime.MinValue, DateTime.MinValue);
234            }
235        }
236
237        private void GetFileTime(out DateTime creationTime, out DateTime lastAccess,
238            out DateTime lastWrite)
239        {
240            SafeFileHandle handle = exclusiveHandle;
241            bool ownsHandle = false;
242            try
243            {
244                if (handle == null || handle.IsClosed || handle.IsInvalid)
245                {
246                    handle = fileHandle;
247                    ownsHandle = true;
248                }
249            }
250            catch (ObjectDisposedException)
251            {
252                handle = fileHandle;
253                ownsHandle = true;
254            }
255
256            try
257            {
258                KernelApi.GetFileTime(handle, out creationTime, out lastAccess, out lastWrite);
259            }
260            finally
261            {
262                if (ownsHandle)
263                    handle.Close();
264            }
265        }
266
267        private void SetFileTime(DateTime creationTime, DateTime lastAccess, DateTime lastWrite)
268        {
269            SafeFileHandle handle = exclusiveHandle;
270            bool ownsHandle = false;
271            try
272            {
273                if (handle == null || handle.IsClosed || handle.IsInvalid)
274                {
275                    handle = fileHandle;
276                    ownsHandle = true;
277                }
278            }
279            catch (ObjectDisposedException)
280            {
281                handle = fileHandle;
282                ownsHandle = true;
283            }
284
285            try
286            {
287                KernelApi.SetFileTime(handle, creationTime, lastAccess, lastWrite);
288            }
289            finally
290            {
291                if (ownsHandle)
292                    handle.Close();
293            }
294        }
295
296        /// <summary>
297        /// Permanently deletes a file.
298        /// </summary>
299        public void Delete()
300        {
301            if (streamName == null)
302                File.Delete();
303            else
304                if (!KernelApi.NativeMethods.DeleteFile(FullName))
305                    throw KernelApi.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
306        }
307
308        /// <summary>
309        /// Opens a file in the specified mode.
310        /// </summary>
311        /// <param name="mode">A System.IO.FileMode constant specifying the mode
312        /// (for example, Open or Append) in which to open the file.</param>
313        /// <returns>A file opened in the specified mode, with read/write access,
314        /// unshared, and no special file options.</returns>
315        public FileStream Open(FileMode mode)
316        {
317            return Open(mode, FileAccess.ReadWrite, FileShare.None, FileOptions.None);
318        }
319
320        /// <summary>
321        /// Opens a file in the specified mode with read, write, or read/write access.
322        /// </summary>
323        /// <param name="mode">A System.IO.FileMode constant specifying the mode
324        /// (for example, Open or Append) in which to open the file.</param>
325        /// <param name="access">A System.IO.FileAccess constant specifying whether
326        /// to open the file with Read, Write, or ReadWrite file access.</param>
327        /// <returns>A System.IO.FileStream object opened in the specified mode
328        /// and access, unshared, and no special file options.</returns>
329        public FileStream Open(FileMode mode, FileAccess access)
330        {
331            return Open(mode, access, FileShare.None, FileOptions.None);
332        }
333
334        /// <summary>
335        /// Opens a file in the specified mode with read, write, or read/write access
336        /// and the specified sharing option.
337        /// </summary>
338        /// <param name="mode">A System.IO.FileMode constant specifying the mode
339        /// (for example, Open or Append) in which to open the file.</param>
340        /// <param name="access">A System.IO.FileAccess constant specifying whether
341        /// to open the file with Read, Write, or ReadWrite file access.</param>
342        /// <param name="share">A System.IO.FileShare constant specifying the type
343        /// of access other FileStream objects have to this file.</param>
344        /// <returns>A System.IO.FileStream object opened with the specified mode,
345        /// access, sharing options, and no special file options.</returns>
346        public FileStream Open(FileMode mode, FileAccess access, FileShare share)
347        {
348            return Open(mode, access, share, FileOptions.None);
349        }
350
351        /// <summary>
352        /// Opens a file in the specified mode with read, write, or read/write access,
353        /// the specified sharing option, and other advanced options.
354        /// </summary>
355        /// <param name="mode">A System.IO.FileMode constant specifying the mode
356        /// (for example, Open or Append) in which to open the file.</param>
357        /// <param name="access">A System.IO.FileAccess constant specifying whether
358        /// to open the file with Read, Write, or ReadWrite file access.</param>
359        /// <param name="share">A System.IO.FileShare constant specifying the type
360        /// of access other FileStream objects have to this file.</param>
361        /// <param name="options">The System.IO.FileOptions constant specifying
362        /// the advanced file options to use when opening the file.</param>
363        /// <returns>A System.IO.FileStream object opened with the specified mode,
364        /// access, sharing options, and special file options.</returns>
365        public FileStream Open(FileMode mode, FileAccess access, FileShare share,
366            FileOptions options)
367        {
368            SafeFileHandle handle = OpenHandle(mode, access, share, options);
369
370            //Check that the handle is valid
371            if (handle.IsInvalid)
372                throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
373
374            //Return the FileStream
375            return new FileStream(handle, access);
376        }
377
378        private SafeFileHandle OpenHandle(FileMode mode, FileAccess access, FileShare share,
379            FileOptions options)
380        {
381            //Access mode
382            uint iAccess = 0;
383            switch (access)
384            {
385                case FileAccess.Read:
386                    iAccess = KernelApi.NativeMethods.GENERIC_READ;
387                    break;
388                case FileAccess.ReadWrite:
389                    iAccess = KernelApi.NativeMethods.GENERIC_READ |
390                        KernelApi.NativeMethods.GENERIC_WRITE;
391                    break;
392                case FileAccess.Write:
393                    iAccess = KernelApi.NativeMethods.GENERIC_WRITE;
394                    break;
395            }
396
397            //Sharing mode
398            if ((share & FileShare.Inheritable) != 0)
399                throw new NotSupportedException("Inheritable handles are not supported.");
400
401            //Advanced options
402            if ((options & FileOptions.Asynchronous) != 0)
403                throw new NotSupportedException("Asynchronous handles are not implemented.");
404           
405            //Create the handle
406            SafeFileHandle result = KernelApi.NativeMethods.CreateFile(FullName, iAccess,
407                (uint)share, IntPtr.Zero, (uint)mode, (uint)options, IntPtr.Zero);
408            if (result.IsInvalid)
409                throw KernelApi.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
410
411            //Cache the handle if we have an exclusive handle - this is used for things like
412            //file times.
413            if (share == FileShare.None)
414                exclusiveHandle = result;
415            return result;
416        }
417
418        /// <summary>
419        /// Returns the path as a string.
420        /// </summary>
421        /// <returns>A string representing the path.</returns>
422        public override string ToString()
423        {
424            return FullName;
425        }
426
427        /// <summary>
428        /// Retrieves a file handle with read access and with all sharing enabled.
429        /// </summary>
430        private SafeFileHandle fileHandle
431        {
432            get
433            {
434                return OpenHandle(FileMode.Open, FileAccess.Read, FileShare.ReadWrite |
435                    FileShare.Delete, FileOptions.None);
436            }
437        }
438       
439        /// <summary>
440        /// Cached exclusive file handle. This is used for setting file access times
441        /// </summary>
442        private SafeFileHandle exclusiveHandle;
443        private string fileName;
444        private string streamName;
445    }
446}
Note: See TracBrowser for help on using the repository browser.