source: branches/eraser6/Util/StreamInfo.cs @ 421

Revision 421, 17.2 KB checked in by lowjoel, 6 years ago (diff)

Fixed a wrong function declaration.

  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008 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 : FileSystemInfo
34    {
35        /// <summary>
36        /// Initializes a new instance of the Eraser.Util.FileInfo 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            OriginalPath = path;
44            FullPath = path;
45
46            //Separate the path into the ADS and the file.
47            if (path.IndexOf(':') != path.LastIndexOf(':'))
48            {
49                int streamNameColon = path.IndexOf(':', path.IndexOf(':') + 1);
50                fileName = path.Substring(0, streamNameColon);
51                streamName = path.Substring(streamNameColon + 1);
52            }
53            else
54                fileName = path;
55        }
56
57        /// <summary>
58        /// Gets an instance of the parent directory.
59        /// </summary>
60        public DirectoryInfo Directory
61        {
62            get
63            {
64                return new DirectoryInfo(DirectoryName);
65            }
66        }
67
68        /// <summary>
69        /// Gets a string representing the containing directory's full path.
70        /// </summary>
71        public string DirectoryName
72        {
73            get
74            {
75                return fileName.Substring(0, fileName.LastIndexOf(Path.DirectorySeparatorChar) + 1);
76            }
77        }
78
79        /// <summary>
80        /// Gets an instance of the main file. If this object refers to an ADS, the
81        /// result is null.
82        /// </summary>
83        public FileInfo File
84        {
85            get
86            {
87                if (streamName == null)
88                    return new FileInfo(fileName);
89                return null;
90            }
91        }
92       
93        /// <summary>
94        /// Gets a value indicating whether the stream exists.
95        /// </summary>
96        public override bool Exists
97        {
98            get
99            {
100                using (SafeFileHandle handle = fileHandle)
101                    return !(handle.IsInvalid &&
102                        Marshal.GetLastWin32Error() == 2 /*ERROR_FILE_NOT_FOUND*/);
103            }
104        }
105
106        /// <summary>
107        /// Gets or sets a value that determines if the current file is read only.
108        /// </summary>
109        public bool IsReadOnly
110        {
111            get
112            {
113                uint attributes = GetFileAttributes(fileName);
114                return (attributes & FILE_ATTRIBUTE_READONLY) != 0;
115            }
116
117            set
118            {
119                if (value)
120                    SetFileAttributes(fileName, GetFileAttributes(fileName) | FILE_ATTRIBUTE_READONLY);
121                else
122                    SetFileAttributes(fileName, GetFileAttributes(fileName) & ~FILE_ATTRIBUTE_READONLY);
123            }
124        }
125
126        /// <summary>
127        /// Gets the size of the current stream.
128        /// </summary>
129        public long Length
130        {
131            get
132            {
133                long fileSize;
134                using (SafeFileHandle handle = fileHandle)
135                    if (GetFileSizeEx(handle, out fileSize))
136                        return fileSize;
137
138                throw new IOException("The size of the stream could not be retrieved",
139                    new Win32Exception(Marshal.GetLastWin32Error()));
140            }
141        }
142
143        /// <summary>
144        /// Gets the name of the file.
145        /// </summary>
146        public override string Name
147        {
148            get { return fileName; }
149        }
150
151        /// <summary>
152        /// Permanently deletes a file.
153        /// </summary>
154        public override void Delete()
155        {
156            throw new NotImplementedException("Deleting streams are not implemented");
157        }
158
159        /// <summary>
160        /// Opens a file in the specified mode.
161        /// </summary>
162        /// <param name="mode">A System.IO.FileMode constant specifying the mode
163        /// (for example, Open or Append) in which to open the file.</param>
164        /// <returns>A file opened in the specified mode, with read/write access,
165        /// unshared, and no special file options.</returns>
166        public FileStream Open(FileMode mode)
167        {
168            return Open(mode, FileAccess.ReadWrite, FileShare.None, FileOptions.None);
169        }
170
171        /// <summary>
172        /// Opens a file in the specified mode with read, write, or read/write access.
173        /// </summary>
174        /// <param name="mode">A System.IO.FileMode constant specifying the mode
175        /// (for example, Open or Append) in which to open the file.</param>
176        /// <param name="access">A System.IO.FileAccess constant specifying whether
177        /// to open the file with Read, Write, or ReadWrite file access.</param>
178        /// <returns>A System.IO.FileStream object opened in the specified mode
179        /// and access, unshared, and no special file options.</returns>
180        public FileStream Open(FileMode mode, FileAccess access)
181        {
182            return Open(mode, access, FileShare.None, FileOptions.None);
183        }
184
185        /// <summary>
186        /// Opens a file in the specified mode with read, write, or read/write access
187        /// and the specified sharing option.
188        /// </summary>
189        /// <param name="mode">A System.IO.FileMode constant specifying the mode
190        /// (for example, Open or Append) in which to open the file.</param>
191        /// <param name="access">A System.IO.FileAccess constant specifying whether
192        /// to open the file with Read, Write, or ReadWrite file access.</param>
193        /// <param name="share">A System.IO.FileShare constant specifying the type
194        /// of access other FileStream objects have to this file.</param>
195        /// <returns>A System.IO.FileStream object opened with the specified mode,
196        /// access, sharing options, and no special file options.</returns>
197        public FileStream Open(FileMode mode, FileAccess access, FileShare share)
198        {
199            return Open(mode, access, share, FileOptions.None);
200        }
201
202        /// <summary>
203        /// Opens a file in the specified mode with read, write, or read/write access,
204        /// the specified sharing option, and other advanced options.
205        /// </summary>
206        /// <param name="mode">A System.IO.FileMode constant specifying the mode
207        /// (for example, Open or Append) in which to open the file.</param>
208        /// <param name="access">A System.IO.FileAccess constant specifying whether
209        /// to open the file with Read, Write, or ReadWrite file access.</param>
210        /// <param name="share">A System.IO.FileShare constant specifying the type
211        /// of access other FileStream objects have to this file.</param>
212        /// <param name="options">The System.IO.FileOptions constant specifying
213        /// the advanced file options to use when opening the file.</param>
214        /// <returns>A System.IO.FileStream object opened with the specified mode,
215        /// access, sharing options, and special file options.</returns>
216        public FileStream Open(FileMode mode, FileAccess access, FileShare share,
217            FileOptions options)
218        {
219            //Access mode
220            uint iAccess = 0;
221            switch (access)
222            {
223                case FileAccess.Read:
224                    iAccess = Util.File.GENERIC_READ;
225                    break;
226                case FileAccess.ReadWrite:
227                    iAccess = Util.File.GENERIC_READ | Util.File.GENERIC_WRITE;
228                    break;
229                case FileAccess.Write:
230                    iAccess = Util.File.GENERIC_WRITE;
231                    break;
232            }
233
234            //File mode
235            uint iMode = 0;
236            switch (mode)
237            {
238                case FileMode.Append:
239                    iMode = Util.File.OPEN_EXISTING;
240                    break;
241                case FileMode.Create:
242                    iMode = Util.File.CREATE_ALWAYS;
243                    break;
244                case FileMode.CreateNew:
245                    iMode = Util.File.CREATE_NEW;
246                    break;
247                case FileMode.Open:
248                    iMode = Util.File.OPEN_EXISTING;
249                    break;
250                case FileMode.OpenOrCreate:
251                    iMode = Util.File.OPEN_ALWAYS;
252                    break;
253                case FileMode.Truncate:
254                    iMode = Util.File.TRUNCATE_EXISTING;
255                    break;
256            }
257
258            //Sharing mode
259            uint iShare = 0;
260            switch (share)
261            {
262                case FileShare.Delete:
263                    iShare = Util.File.FILE_SHARE_DELETE;
264                    break;
265                case FileShare.Inheritable:
266                    throw new NotImplementedException("Inheritable handles are not implemented.");
267                case FileShare.None:
268                    iShare = 0;
269                    break;
270                case FileShare.Read:
271                    iShare = Util.File.FILE_SHARE_READ;
272                    break;
273                case FileShare.ReadWrite:
274                    iShare = Util.File.FILE_SHARE_READ | Util.File.FILE_SHARE_WRITE;
275                    break;
276                case FileShare.Write:
277                    iShare = Util.File.FILE_SHARE_WRITE;
278                    break;
279            }
280
281            //Advanced options
282            uint iOptions = 0;
283            if ((options & FileOptions.Asynchronous) != 0)
284                throw new NotImplementedException("Asynchronous handles are not implemented.");
285            if ((options & FileOptions.Encrypted) != 0)
286                iOptions |= FILE_ATTRIBUTE_ENCRYPTED;
287            if ((options & FileOptions.DeleteOnClose) != 0)
288                iOptions |= Util.File.FILE_FLAG_DELETE_ON_CLOSE;
289            if ((options & FileOptions.WriteThrough) != 0)
290                iOptions |= Util.File.FILE_FLAG_WRITE_THROUGH;
291            if ((options & FileOptions.RandomAccess) != 0)
292                iOptions |= Util.File.FILE_FLAG_RANDOM_ACCESS;
293            if ((options & FileOptions.SequentialScan) != 0)
294                iOptions |= Util.File.FILE_FLAG_SEQUENTIAL_SCAN;
295
296            //Create the handle
297            SafeFileHandle handle = Util.File.CreateFile(FullPath, iAccess, iShare,
298                IntPtr.Zero, iMode, iOptions, IntPtr.Zero);
299
300            //If CreateNew was used and the file exists, throw the IOException.
301            if (handle.IsInvalid && Marshal.GetLastWin32Error() == 80 /*ERROR_FILE_EXISTS*/ &&
302                mode == FileMode.CreateNew)
303            {
304                Win32Exception exception = new Win32Exception(Marshal.GetLastWin32Error());
305                throw new IOException(exception.Message, exception);
306            }
307
308            //If Open was used and the file does not exist, throw the FileNotFileException.
309            if (handle.IsInvalid && Marshal.GetLastWin32Error() == 2 /*ERROR_FILE_NOT_FOUND*/ &&
310                mode == FileMode.Open)
311            {
312                Win32Exception exception = new Win32Exception(Marshal.GetLastWin32Error());
313                throw new FileNotFoundException(exception.Message, exception);
314            }
315
316            //Other errors.
317            if (handle.IsInvalid)
318            {
319                Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());
320                switch (Marshal.GetLastWin32Error())
321                {
322                    case 1008:                                                  //ERROR_NO_TOKEN
323                    case 5:                                                     //ERROR_ACCESS_DENIED
324                        throw new UnauthorizedAccessException(ex.Message);
325                    case 32:
326                        throw new IOException(ex.Message);
327                    default:
328                        throw ex;
329                }
330            }
331
332            //Return the FileStream
333            return new FileStream(handle, access);
334        }
335
336        /// <summary>
337        /// Returns the path as a string.
338        /// </summary>
339        /// <returns>A string representing the path.</returns>
340        public override string ToString()
341        {
342            if (streamName.Length != 0)
343                return fileName + ':' + streamName;
344            return fileName;
345        }
346
347        private SafeFileHandle fileHandle
348        {
349            get
350            {
351                //Create the handle
352                return Util.File.CreateFile(FullPath, 0,
353                    Util.File.FILE_SHARE_READ | Util.File.FILE_SHARE_WRITE | Util.File.FILE_SHARE_DELETE,
354                    IntPtr.Zero, Util.File.OPEN_EXISTING, 0, IntPtr.Zero);
355            }
356        }
357        private string fileName;
358        private string streamName;
359
360        /// <summary>
361        /// Retrieves a set of FAT file system attributes for a specified file or
362        /// directory.
363        /// </summary>
364        /// <param name="lpFileName">The name of the file or directory.</param>
365        /// <returns>If the function succeeds, the return value contains the attributes
366        /// of the specified file or directory.
367        ///
368        /// If the function fails, the return value is INVALID_FILE_ATTRIBUTES.
369        /// To get extended error information, call Marshal.GetLastWin32Error.
370        ///
371        /// The attributes can be one or more of the FILE_ATTRIBUTE_* values.</returns>
372        private static uint GetFileAttributes(string lpFileName)
373        {
374            return GetFileAttributesInternal("\\\\?\\" + lpFileName);
375        }
376
377        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode,
378            EntryPoint = "GetFileAttributes")]
379        private static extern uint GetFileAttributesInternal(string lpFileName);
380
381        /// <summary>
382        /// Sets the attributes for a file or directory.
383        /// </summary>
384        /// <param name="lpFileName">The name of the file whose attributes are
385        /// to be set.</param>
386        /// <param name="dwFileAttributes">The file attributes to set for the file.
387        /// This parameter can be one or more of the FILE_ATTRIBUTE_* values.
388        /// However, all other values override FILE_ATTRIBUTE_NORMAL.</param>
389        /// <returns>If the function succeeds, the return value is nonzero.
390        ///
391        /// If the function fails, the return value is zero. To get extended error
392        /// information, call Marshal.GetLastWin32Error.</returns>
393        private static bool SetFileAttributes(string lpFileName, uint dwFileAttributes)
394        {
395            return SetFileAttributesInternal("\\\\?\\" + lpFileName, dwFileAttributes);
396        }
397
398        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode,
399            EntryPoint = "SetFileAttributes")]
400        [return: MarshalAs(UnmanagedType.Bool)]
401        private static extern bool SetFileAttributesInternal(string lpFileName,
402            uint dwFileAttributes);
403
404        /// <summary>
405        /// A file or directory that is an archive file or directory. Applications
406        /// use this attribute to mark files for backup or removal.
407        /// </summary>
408        const uint FILE_ATTRIBUTE_ARCHIVE = 0x20;
409
410        /// <summary>
411        /// A file or directory that is compressed.
412        ///
413        /// For a file, all of the data in the file is compressed. For a directory,
414        /// compression is the default for newly created files and subdirectories.
415        /// </summary>
416        const uint FILE_ATTRIBUTE_COMPRESSED = 0x800;
417
418        /// <summary>
419        /// Reserved; do not use.
420        /// </summary>
421        const int FILE_ATTRIBUTE_DEVICE = 0x40;
422
423        /// <summary>
424        /// The handle that identifies a directory.
425        /// </summary>
426        const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
427
428        /// <summary>
429        /// A file or directory that is encrypted.
430        ///
431        /// For a file, all data streams in the file are encrypted.
432        /// For a directory, encryption is the default for newly created files
433        /// and subdirectories.
434        /// </summary>
435        const uint FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
436
437        /// <summary>
438        /// The file or directory is hidden. It is not included in an ordinary
439        /// directory listing.
440        /// </summary>
441        const uint FILE_ATTRIBUTE_HIDDEN = 0x2;
442
443        /// <summary>
444        /// A file or directory that does not have other attributes set. This attribute
445        /// is valid only when used alone.
446        /// </summary>
447        const uint FILE_ATTRIBUTE_NORMAL = 0x80;
448
449        /// <summary>
450        /// The file is not to be indexed by the content indexing service.
451        /// </summary>
452        const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
453
454        /// <summary>
455        /// The data of a file is not available immediately. This attribute indicates
456        /// that the file data is physically moved to offline storage. This attribute
457        /// is used by Remote Storage, which is the hierarchical storage management
458        /// software. Applications should not arbitrarily change this attribute.
459        /// </summary>
460        const uint FILE_ATTRIBUTE_OFFLINE = 0x1000;
461
462        /// <summary>
463        /// A file or directory that is read-only.
464        ///
465        /// For a file, applications can read the file, but cannot write to it or
466        /// delete it.
467        ///
468        /// For a directory, applications cannot delete it.
469        /// </summary>
470        const uint FILE_ATTRIBUTE_READONLY = 0x1;
471
472        /// <summary>
473        /// A file or directory that has an associated reparse point, or a file
474        /// that is a symbolic link.
475        /// </summary>
476        const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
477
478        /// <summary>
479        /// A file that is a sparse file.
480        /// </summary>
481        const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
482
483        /// <summary>
484        /// A file or directory that the operating system uses a part of, or uses
485        /// exclusively.
486        /// </summary>
487        const uint FILE_ATTRIBUTE_SYSTEM = 0x4;
488
489        /// <summary>
490        /// A file that is being used for temporary storage.
491        ///
492        /// File systems avoid writing data back to mass storage if sufficient cache
493        /// memory is available, because typically, an application deletes a temporary
494        /// file after the handle is closed. In that scenario, the system can entirely
495        /// avoid writing the data. Otherwise, the data is written after the handle
496        /// is closed.
497        /// </summary>
498        const uint FILE_ATTRIBUTE_TEMPORARY = 0x100;
499
500        /// <summary>
501        /// A file is a virtual file.
502        /// </summary>
503        const uint FILE_ATTRIBUTE_VIRTUAL = 0x10000;
504
505        /// <summary>
506        /// Retrieves the size of the specified file.
507        /// </summary>
508        /// <param name="hFile">A handle to the file. The handle must have been
509        /// created with either the GENERIC_READ or GENERIC_WRITE access right.
510        /// For more information, see File Security and Access Rights.</param>
511        /// <param name="lpFileSize">A reference to a long that receives the file
512        /// size, in bytes.</param>
513        /// <returns>If the function succeeds, the return value is nonzero.
514        ///
515        /// If the function fails, the return value is zero. To get extended error
516        /// information, call Marshal.GetLastWin32Error.</returns>
517        [DllImport("Kernel32.dll", SetLastError = true)]
518        [return: MarshalAs(UnmanagedType.Bool)]
519        private static extern bool GetFileSizeEx(SafeFileHandle hFile, out long lpFileSize);
520    }
521}
Note: See TracBrowser for help on using the repository browser.