source: trunk/eraser/Eraser.Util/StreamInfo.cs @ 2281

Revision 2281, 10.7 KB checked in by lowjoel, 4 years ago (diff)

Forward-port from Eraser 6.0: 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    /// <summary>
34    /// Provides methods for the deletion, and opening of file alternate data streams,
35    /// and aids in the creation of <see cref="System.IO.FileStream"/> objects.
36    /// </summary>
37    public class StreamInfo : FileSystemInfo
38    {
39        /// <summary>
40        /// Constructor.
41        /// </summary>
42        /// <param name="filename">The fully qualified name of the new file, or
43        /// the relative file name.</param>
44        public StreamInfo(string filename)
45            : this(filename, null)
46        {
47        }
48
49        /// <summary>
50        /// Constructor.
51        /// </summary>
52        /// <param name="filename">The path to the file.</param>
53        /// <param name="streamName">The name of the alternate data stream, or null
54        /// to refer to the unnamed stream.</param>
55        public StreamInfo(string filename, string streamName)
56        {
57            OriginalPath = filename;
58            FullPath = Path.GetFullPath(filename);
59            FileName = FullPath;
60            StreamName = streamName;
61
62            if (!string.IsNullOrEmpty(streamName))
63            {
64                OriginalPath += ":" + streamName;
65                FullPath += ":" + streamName;
66            }
67
68            Refresh();
69        }
70
71        /// <summary>
72        /// The full name of the stream, including the stream name if provided.
73        /// </summary>
74        public override string FullName
75        {
76            get
77            {
78                return FullPath;
79            }
80        }
81
82        /// <summary>
83        /// Gets a value indicating whether a file exists.
84        /// </summary>
85        public override bool Exists
86        {
87            get
88            {
89                bool result = System.IO.File.Exists(FullName);
90                return result &&
91                    (string.IsNullOrEmpty(StreamName) || true/*TODO: verify the ADS exists*/);
92            }
93        }
94
95        /// <summary>
96        /// Gets a string representing the directory's full path.
97        /// </summary>
98        public String DirectoryName
99        {
100            get
101            {
102                return Path.GetDirectoryName(FullPath);
103            }
104        }
105
106        /// <summary>
107        /// Gets an instance of the parent directory.
108        /// </summary>
109        public DirectoryInfo Directory
110        {
111            get
112            {
113                return new DirectoryInfo(DirectoryName);
114            }
115        } 
116
117        /// <summary>
118        /// Gets the file which contains this stream.
119        /// </summary>
120        public FileInfo File
121        {
122            get
123            {
124                return new FileInfo(FileName);
125            }
126        }
127
128        /// <summary>
129        /// The full path to the file we are encapsulating.
130        /// </summary>
131        public string FileName
132        {
133            get;
134            private set;
135        }
136
137        /// <summary>
138        /// Gets the name of the stream.
139        /// </summary>
140        public override string Name
141        {
142            get { return StreamName; }
143        }
144
145        /// <summary>
146        /// Gets or sets a value that determines if the current file is read only.
147        /// </summary>
148        public bool IsReadOnly
149        {
150            get { return (Attributes & FileAttributes.ReadOnly) != 0; }
151            set
152            {
153                Attributes = value ?
154                    (Attributes | FileAttributes.ReadOnly) :
155                    (Attributes & ~FileAttributes.ReadOnly);
156            }
157        }
158
159        /// <summary>
160        /// Gets the size, in bytes, of the current stream.
161        /// </summary>
162        public long Length
163        {
164            get
165            {
166                try
167                {
168                    using (SafeFileHandle handle = OpenHandle(
169                        FileMode.Open, FileAccess.Read, FileShare.ReadWrite, FileOptions.None))
170                    {
171                        long fileSize;
172                        if (NativeMethods.GetFileSizeEx(handle, out fileSize))
173                            return fileSize;
174                    }
175                }
176                catch (UnauthorizedAccessException)
177                {
178                    //Swallow: we just return 0 in this situation as there's nothing we can
179                    //do about this error.
180                }
181
182                return 0;
183            }
184        }
185
186        /// <summary>
187        /// Creates the file if it already does not exist, then creates the alternate
188        /// data stream.
189        /// </summary>
190        public FileStream Create()
191        {
192            return Open(FileMode.Create, FileAccess.ReadWrite, FileShare.None, FileOptions.None);
193        }
194
195        /// <summary>
196        /// Permanently deletes the stream. If this refers to the unnamed stream, all
197        /// alternate data streams are also deleted.
198        /// </summary>
199        public override void Delete()
200        {
201            if (!NativeMethods.DeleteFile(FullName))
202            {
203                int errorCode = Marshal.GetLastWin32Error();
204                switch (errorCode)
205                {
206                    case Win32ErrorCode.PathNotFound:
207                        break;
208                    default:
209                        throw Win32ErrorCode.GetExceptionForWin32Error(errorCode);
210                }
211            }
212        }
213
214        /// <summary>
215        /// Opens a file in the specified mode.
216        /// </summary>
217        /// <param name="mode">A System.IO.FileMode constant specifying the mode
218        /// (for example, Open or Append) in which to open the file.</param>
219        /// <returns>A file opened in the specified mode, with read/write access,
220        /// unshared, and no special file options.</returns>
221        public FileStream Open(FileMode mode)
222        {
223            return Open(mode, FileAccess.ReadWrite, FileShare.None, FileOptions.None);
224        }
225
226        /// <summary>
227        /// Opens a file in the specified mode with read, write, or read/write access.
228        /// </summary>
229        /// <param name="mode">A System.IO.FileMode constant specifying the mode
230        /// (for example, Open or Append) in which to open the file.</param>
231        /// <param name="access">A System.IO.FileAccess constant specifying whether
232        /// to open the file with Read, Write, or ReadWrite file access.</param>
233        /// <returns>A System.IO.FileStream object opened in the specified mode
234        /// and access, unshared, and no special file options.</returns>
235        public FileStream Open(FileMode mode, FileAccess access)
236        {
237            return Open(mode, access, FileShare.None, FileOptions.None);
238        }
239
240        /// <summary>
241        /// Opens a file in the specified mode with read, write, or read/write access
242        /// and the specified sharing option.
243        /// </summary>
244        /// <param name="mode">A System.IO.FileMode constant specifying the mode
245        /// (for example, Open or Append) in which to open the file.</param>
246        /// <param name="access">A System.IO.FileAccess constant specifying whether
247        /// to open the file with Read, Write, or ReadWrite file access.</param>
248        /// <param name="share">A System.IO.FileShare constant specifying the type
249        /// of access other FileStream objects have to this file.</param>
250        /// <returns>A System.IO.FileStream object opened with the specified mode,
251        /// access, sharing options, and no special file options.</returns>
252        public FileStream Open(FileMode mode, FileAccess access, FileShare share)
253        {
254            return Open(mode, access, share, FileOptions.None);
255        }
256
257        /// <summary>
258        /// Opens a file in the specified mode with read, write, or read/write access,
259        /// the specified sharing option, and other advanced options.
260        /// </summary>
261        /// <param name="mode">A System.IO.FileMode constant specifying the mode
262        /// (for example, Open or Append) in which to open the file.</param>
263        /// <param name="access">A System.IO.FileAccess constant specifying whether
264        /// to open the file with Read, Write, or ReadWrite file access.</param>
265        /// <param name="share">A System.IO.FileShare constant specifying the type
266        /// of access other FileStream objects have to this file.</param>
267        /// <param name="options">The System.IO.FileOptions constant specifying
268        /// the advanced file options to use when opening the file.</param>
269        /// <returns>A System.IO.FileStream object opened with the specified mode,
270        /// access, sharing options, and special file options.</returns>
271        public FileStream Open(FileMode mode, FileAccess access, FileShare share,
272            FileOptions options)
273        {
274            SafeFileHandle handle = OpenHandle(mode, access, share, options);
275
276            //Check that the handle is valid
277            if (handle.IsInvalid)
278                throw Win32ErrorCode.GetExceptionForWin32Error(Marshal.GetLastWin32Error());
279
280            //Return the FileStream
281            return new FileStream(handle, access);
282        }
283
284        /// <summary>
285        /// Creates a read-only System.IO.FileStream.
286        /// </summary>
287        /// <returns>A new read-only System.IO.FileStream object.</returns>
288        public FileStream OpenRead()
289        {
290            return Open(FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.None);
291        }
292
293        /// <summary>
294        /// Creates a write-only System.IO.FileStream.
295        /// </summary>
296        /// <returns>A new write-only unshared System.IO.FileStream object.</returns>
297        public FileStream OpenWrite()
298        {
299            return Open(FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, FileOptions.None);
300        }
301
302        private SafeFileHandle OpenHandle(FileMode mode, FileAccess access, FileShare share,
303            FileOptions options)
304        {
305            //Access mode
306            uint iAccess = 0;
307            switch (access)
308            {
309                case FileAccess.Read:
310                    iAccess = NativeMethods.GENERIC_READ;
311                    break;
312                case FileAccess.ReadWrite:
313                    iAccess = NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE;
314                    break;
315                case FileAccess.Write:
316                    iAccess = NativeMethods.GENERIC_WRITE;
317                    break;
318            }
319
320            return OpenHandle(mode, iAccess, share, options);
321        }
322
323        private SafeFileHandle OpenHandle(FileMode mode, uint access, FileShare share,
324            FileOptions options)
325        {
326            //Sharing mode
327            if ((share & FileShare.Inheritable) != 0)
328                throw new NotSupportedException("Inheritable handles are not supported.");
329
330            //Advanced options
331            if ((options & FileOptions.Asynchronous) != 0)
332                throw new NotSupportedException("Asynchronous handles are not implemented.");
333
334            //Create the handle
335            SafeFileHandle result = NativeMethods.CreateFile(FullName, access,
336                (uint)share, IntPtr.Zero, (uint)mode, (uint)options, IntPtr.Zero);
337            if (result.IsInvalid)
338            {
339                int errorCode = Marshal.GetLastWin32Error();
340                result.Close();
341                throw Win32ErrorCode.GetExceptionForWin32Error(errorCode);
342            }
343
344            return result;
345        }
346
347        public void SetTimes(DateTime updateTime, DateTime createdTime, DateTime lastModifiedTime,
348            DateTime lastAccessedTime)
349        {
350            using (SafeFileHandle streamHandle = OpenHandle(FileMode.Open,
351                    NativeMethods.FILE_WRITE_ATTRIBUTES, FileShare.ReadWrite,
352                    FileOptions.None))
353            {
354                ExtensionMethods.Methods.SetTimes(streamHandle, updateTime, createdTime,
355                    lastModifiedTime, lastAccessedTime);
356            }
357        }
358
359        /// <summary>
360        /// Returns the path as a string.
361        /// </summary>
362        /// <returns>A string containing the path given to the constructor.</returns>
363        public override string ToString()
364        {
365            return OriginalPath;
366        }
367
368        /// <summary>
369        /// The name of the stream we are encapsulating.
370        /// </summary>
371        private string StreamName;
372    }
373}
Note: See TracBrowser for help on using the repository browser.