@@ -159,29 +159,24 @@ public static void Create(this DirectoryInfo directoryInfo, DirectorySecurity di
159
159
/// <param name="share">One of the enumeration values for controlling the kind of access other FileStream objects can have to the same file.</param>
160
160
/// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
161
161
/// <param name="options">One of the enumeration values that describes how to create or overwrite the file.</param>
162
- /// <param name="fileSecurity">An object that determines the access control and audit security for the file.</param>
162
+ /// <param name="fileSecurity">An optional object that determines the access control and audit security for the file.</param>
163
163
/// <returns>A file stream for the newly created file.</returns>
164
164
/// <exception cref="ArgumentException">The <paramref name="rights" /> and <paramref name="mode" /> combination is invalid.</exception>
165
- /// <exception cref="ArgumentNullException"><paramref name="fileInfo" /> or <paramref name="fileSecurity" /> is <see langword="null" />.</exception>
165
+ /// <exception cref="ArgumentNullException"><paramref name="fileInfo" /> is <see langword="null" />.</exception>
166
166
/// <exception cref="ArgumentOutOfRangeException"><paramref name="mode" /> or <paramref name="share" /> are out of their legal enum range.
167
167
///-or-
168
168
/// <paramref name="bufferSize" /> is not a positive number.</exception>
169
169
/// <exception cref="DirectoryNotFoundException">Could not find a part of the path.</exception>
170
170
/// <exception cref="IOException">An I/O error occurs.</exception>
171
171
/// <exception cref="UnauthorizedAccessException">Access to the path is denied.</exception>
172
172
/// <remarks>This extension method was added to .NET Core to bring the functionality that was provided by the `System.IO.FileStream.#ctor(System.String,System.IO.FileMode,System.Security.AccessControl.FileSystemRights,System.IO.FileShare,System.Int32,System.IO.FileOptions,System.Security.AccessControl.FileSecurity)` .NET Framework constructor.</remarks>
173
- public static FileStream Create ( this FileInfo fileInfo , FileMode mode , FileSystemRights rights , FileShare share , int bufferSize , FileOptions options , FileSecurity fileSecurity )
173
+ public static FileStream Create ( this FileInfo fileInfo , FileMode mode , FileSystemRights rights , FileShare share , int bufferSize , FileOptions options , FileSecurity ? fileSecurity )
174
174
{
175
175
if ( fileInfo == null )
176
176
{
177
177
throw new ArgumentNullException ( nameof ( fileInfo ) ) ;
178
178
}
179
179
180
- if ( fileSecurity == null )
181
- {
182
- throw new ArgumentNullException ( nameof ( fileSecurity ) ) ;
183
- }
184
-
185
180
// don't include inheritable in our bounds check for share
186
181
FileShare tempshare = share & ~ FileShare . Inheritable ;
187
182
@@ -200,18 +195,33 @@ public static FileStream Create(this FileInfo fileInfo, FileMode mode, FileSyste
200
195
throw new ArgumentOutOfRangeException ( nameof ( bufferSize ) , SR . ArgumentOutOfRange_NeedPosNum ) ;
201
196
}
202
197
203
- // Do not allow using combinations of non-writing file system rights with writing file modes
198
+ // Do not combine writing modes with exclusively read rights
199
+ // Write contains AppendData, WriteAttributes, WriteData and WriteExtendedAttributes
204
200
if ( ( rights & FileSystemRights . Write ) == 0 &&
205
201
( mode == FileMode . Truncate || mode == FileMode . CreateNew || mode == FileMode . Create || mode == FileMode . Append ) )
206
202
{
207
203
throw new ArgumentException ( SR . Format ( SR . Argument_InvalidFileModeAndFileSystemRightsCombo , mode , rights ) ) ;
208
204
}
209
205
206
+ // Additionally, append is disallowed if any read rights are provided
207
+ // ReadAndExecute contains ExecuteFile, ReadAttributes, ReadData, ReadExtendedAttributes and ReadPermissions
208
+ if ( ( rights & FileSystemRights . ReadAndExecute ) != 0 && mode == FileMode . Append )
209
+ {
210
+ throw new ArgumentException ( SR . Argument_InvalidAppendMode ) ;
211
+ }
212
+
213
+ // Cannot truncate unless all of the write rights are provided
214
+ // Write contains AppendData, WriteAttributes, WriteData and WriteExtendedAttributes
215
+ if ( mode == FileMode . Truncate && ( rights & FileSystemRights . Write ) != FileSystemRights . Write )
216
+ {
217
+ throw new ArgumentException ( SR . Format ( SR . Argument_InvalidFileModeAndFileSystemRightsCombo , mode , rights ) ) ;
218
+ }
219
+
210
220
SafeFileHandle handle = CreateFileHandle ( fileInfo . FullName , mode , rights , share , options , fileSecurity ) ;
211
221
212
222
try
213
223
{
214
- return new FileStream ( handle , GetFileStreamFileAccess ( rights ) , bufferSize , ( options & FileOptions . Asynchronous ) != 0 ) ;
224
+ return new FileStream ( handle , GetFileAccessFromRights ( rights ) , bufferSize , ( options & FileOptions . Asynchronous ) != 0 ) ;
215
225
}
216
226
catch
217
227
{
@@ -258,23 +268,48 @@ public static DirectoryInfo CreateDirectory(this DirectorySecurity directorySecu
258
268
259
269
// In the context of a FileStream, the only ACCESS_MASK ACE rights we care about are reading/writing data and the generic read/write rights.
260
270
// See: https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask
261
- private static FileAccess GetFileStreamFileAccess ( FileSystemRights rights )
271
+ private static FileAccess GetFileAccessFromRights ( FileSystemRights rights )
262
272
{
263
273
FileAccess access = 0 ;
264
- if ( ( rights & FileSystemRights . ReadData ) != 0 || ( ( int ) rights & Interop . Kernel32 . GenericOperations . GENERIC_READ ) != 0 )
274
+
275
+ if ( ( rights & FileSystemRights . FullControl ) != 0 ||
276
+ ( rights & FileSystemRights . Modify ) != 0 )
277
+ {
278
+ return FileAccess . ReadWrite ;
279
+ }
280
+
281
+ if ( ( rights & FileSystemRights . ReadData ) != 0 || // Same as ListDirectory
282
+ ( rights & FileSystemRights . ReadExtendedAttributes ) != 0 ||
283
+ ( rights & FileSystemRights . ExecuteFile ) != 0 || // Same as Traverse
284
+ ( rights & FileSystemRights . ReadAttributes ) != 0 ||
285
+ ( rights & FileSystemRights . ReadPermissions ) != 0 ||
286
+ ( rights & FileSystemRights . TakeOwnership ) != 0 ||
287
+ ( ( int ) rights & Interop . Kernel32 . GenericOperations . GENERIC_READ ) != 0 )
265
288
{
266
289
access = FileAccess . Read ;
267
290
}
268
291
269
- if ( ( rights & FileSystemRights . WriteData ) != 0 || ( ( int ) rights & Interop . Kernel32 . GenericOperations . GENERIC_WRITE ) != 0 )
292
+ if ( ( rights & FileSystemRights . AppendData ) != 0 || // Same as CreateDirectories
293
+ ( rights & FileSystemRights . ChangePermissions ) != 0 ||
294
+ ( rights & FileSystemRights . Delete ) != 0 ||
295
+ ( rights & FileSystemRights . DeleteSubdirectoriesAndFiles ) != 0 ||
296
+ ( rights & FileSystemRights . WriteAttributes ) != 0 ||
297
+ ( rights & FileSystemRights . WriteData ) != 0 || // Same as CreateFiles
298
+ ( rights & FileSystemRights . WriteExtendedAttributes ) != 0 ||
299
+ ( ( int ) rights & Interop . Kernel32 . GenericOperations . GENERIC_WRITE ) != 0 )
300
+ {
301
+ access |= FileAccess . Write ;
302
+ }
303
+
304
+ if ( access == 0 )
270
305
{
271
- access = access == FileAccess . Read ? FileAccess . ReadWrite : FileAccess . Write ;
306
+ throw new ArgumentOutOfRangeException ( nameof ( rights ) ) ;
272
307
}
273
308
274
309
return access ;
275
310
}
276
311
277
- private static unsafe SafeFileHandle CreateFileHandle ( string fullPath , FileMode mode , FileSystemRights rights , FileShare share , FileOptions options , FileSecurity security )
312
+ private static unsafe SafeFileHandle CreateFileHandle ( string fullPath , FileMode mode , FileSystemRights rights , FileShare share , FileOptions options , FileSecurity ? security )
278
313
{
279
314
Debug . Assert ( fullPath != null ) ;
280
315
@@ -291,23 +326,39 @@ private static unsafe SafeFileHandle CreateFileHandle(string fullPath, FileMode
291
326
292
327
SafeFileHandle handle ;
293
328
294
- fixed ( byte * pSecurityDescriptor = security . GetSecurityDescriptorBinaryForm ( ) )
329
+ var secAttrs = new Interop . Kernel32 . SECURITY_ATTRIBUTES
295
330
{
296
- var secAttrs = new Interop . Kernel32 . SECURITY_ATTRIBUTES
331
+ nLength = ( uint ) sizeof ( Interop . Kernel32 . SECURITY_ATTRIBUTES ) ,
332
+ bInheritHandle = ( ( share & FileShare . Inheritable ) != 0 ) ? Interop . BOOL . TRUE : Interop . BOOL . FALSE ,
333
+ } ;
334
+
335
+ if ( security != null )
336
+ {
337
+ fixed ( byte * pSecurityDescriptor = security . GetSecurityDescriptorBinaryForm ( ) )
297
338
{
298
- nLength = ( uint ) sizeof ( Interop . Kernel32 . SECURITY_ATTRIBUTES ) ,
299
- bInheritHandle = ( ( share & FileShare . Inheritable ) != 0 ) ? Interop . BOOL . TRUE : Interop . BOOL . FALSE ,
300
- lpSecurityDescriptor = ( IntPtr ) pSecurityDescriptor
301
- } ;
339
+ secAttrs . lpSecurityDescriptor = ( IntPtr ) pSecurityDescriptor ;
340
+ handle = CreateFileHandleInternal ( fullPath , mode , rights , share , flagsAndAttributes , & secAttrs ) ;
341
+ }
342
+ }
343
+ else
344
+ {
345
+ handle = CreateFileHandleInternal ( fullPath , mode , rights , share , flagsAndAttributes , & secAttrs ) ;
346
+ }
347
+
348
+ return handle ;
302
349
350
+ static unsafe SafeFileHandle CreateFileHandleInternal ( string fullPath , FileMode mode , FileSystemRights rights , FileShare share , int flagsAndAttributes , Interop . Kernel32 . SECURITY_ATTRIBUTES * secAttrs )
351
+ {
352
+ SafeFileHandle handle ;
303
353
using ( DisableMediaInsertionPrompt . Create ( ) )
304
354
{
305
- handle = Interop . Kernel32 . CreateFile ( fullPath , ( int ) rights , share , & secAttrs , mode , flagsAndAttributes , IntPtr . Zero ) ;
355
+ // The Inheritable bit is only set in the SECURITY_ATTRIBUTES struct,
356
+ // and should not be passed to the CreateFile P/Invoke.
357
+ handle = Interop . Kernel32 . CreateFile ( fullPath , ( int ) rights , ( share & ~ FileShare . Inheritable ) , secAttrs , mode , flagsAndAttributes , IntPtr . Zero ) ;
306
358
ValidateFileHandle ( handle , fullPath ) ;
307
359
}
360
+ return handle ;
308
361
}
309
-
310
- return handle ;
311
362
}
312
363
313
364
private static void ValidateFileHandle ( SafeFileHandle handle , string fullPath )
0 commit comments