in TestSuites/FileServer/src/FSA/Adapter/FSAAdapter.cs [512:893]
public MessageStatus CreateFile(
FileAttribute desiredFileAttribute,
CreateOptions createOption,
StreamTypeNameToOpen streamTypeNameToOpen,
FileAccess desiredAccess,
ShareAccess shareAccess,
CreateDisposition createDisposition,
StreamFoundType streamFoundType,
SymbolicLinkType symbolicLinkType,
FileType openFileType,
FileNameStatus fileNameStatus
)
{
gOpenMode = createOption;
gStreamType = (streamTypeNameToOpen == StreamTypeNameToOpen.INDEX_ALLOCATION ? StreamType.DirectoryStream : StreamType.DataStream);
uint createAction = 0;
string randomFile = this.ComposeRandomFileName();
this.fileName = randomFile;
if (fileNameStatus == FileNameStatus.NotPathNameValid)
{
// The loop time is 32
for (int i = 0; i < 32; i++)
{
randomFile += testConfig.GetProperty("InvalidName");
}
}
//Get the SymboLinkFile name on the server if SymbolicLink is required.
else if (symbolicLinkType == SymbolicLinkType.IsSymbolicLink)
{
randomFile = testConfig.GetProperty("SymbolicLinkFile");
if (this.FileSystem == FileSystem.FAT32)
{
site.Assume.Inconclusive("Symbolic Link is not supported by FAT32 system.");
}
}
//Retrieve the existing the folder
else if (createDisposition == CreateDisposition.OPEN
&& openFileType == FileType.DirectoryFile)
{
randomFile = testConfig.GetProperty("ExistingFolder");
}
//Retrieve the existing the file
else if (createDisposition == CreateDisposition.OPEN
&& openFileType == FileType.DataFile)
{
randomFile = testConfig.GetProperty("ExistingFile");
}
//Construct a path name with trailing backslash.
else if ((fileNameStatus == FileNameStatus.BackslashName) &&
(createOption & CreateOptions.NON_DIRECTORY_FILE) != 0)
{
if (fileSystem == Adapter.FileSystem.NTFS || fileSystem == Adapter.FileSystem.REFS)
{
randomFile = randomFile + @"\\\\\\" + "::$DATA";
}
else
{
// For file systems other than NTFS or ReFS, the constructed path name with trailing backslash should not contain "::$DATA"
randomFile = randomFile + @"\\\\\\";
}
}
//Construct a data file for an directory operation to
//trigger the error code OBJECT_NAME_COLLISION
//for creating operation and NOT_A_DIRECTORY for openning operation.
else if ((createOption == CreateOptions.DIRECTORY_FILE) &&
(fileNameStatus == FileNameStatus.OpenFileNotNull) &&
(openFileType != FileType.DirectoryFile))
{
if (createDisposition == CreateDisposition.CREATE)
{
randomFile = testConfig.GetProperty("ExistingFile");
randomFile = randomFile + "::$INDEX_ALLOCATION";
}
else if (createDisposition == CreateDisposition.OPEN)
{
randomFile = testConfig.GetProperty("ExistingFile");
randomFile = randomFile + "::$DATA";
}
}
//Construct a directory file for an data file operation to
//trigger the error code FILE_IS_A_DIRECTORY.
else if ((createOption == CreateOptions.NON_DIRECTORY_FILE) &&
(streamTypeNameToOpen == StreamTypeNameToOpen.NULL) &&
(fileNameStatus != FileNameStatus.FileNameNull) && (openFileType == FileType.DirectoryFile))
{
randomFile = testConfig.GetProperty("ExistingFolder");
}
//Constuct message with CreateOptions.RANDOM_ACCESS and use file name to
//indicate the file type.
else if (createDisposition == CreateDisposition.CREATE
&& (createOption & CreateOptions.RANDOM_ACCESS) != 0)
{
if (streamTypeNameToOpen == StreamTypeNameToOpen.DATA)
{
randomFile = randomFile + "::$DATA";
}
else if (streamTypeNameToOpen == StreamTypeNameToOpen.INDEX_ALLOCATION)
{
randomFile = randomFile + "::$INDEX_ALLOCATION";
}
}
//Construct the Non-Existfile to trigger the message OBJECT_NAME_NOT_FOUND
if (createDisposition == CreateDisposition.OPEN && streamFoundType == StreamFoundType.StreamIsNotFound)
{
randomFile = Guid.NewGuid().ToString();
}
//Construct the Non-Exist folder to trigger the error code OBJECT_PATH_NOT_FOUND
if (createDisposition == CreateDisposition.OPEN && fileNameStatus == FileNameStatus.isprefixLinkNotFound)
{
randomFile = Guid.NewGuid().ToString();
}
if (fileNameStatus == FileNameStatus.StreamTypeNameIsINDEX_ALLOCATION)
{
// Full name of a stream is <filename>:<stream name>:<stream type>
// If any StreamTypeNamei is "$INDEX_ALLOCATION"
// and the corresponding StreamNamei has a value other than an empty string or "$I30",
// the operation SHOULD be failed with STATUS_INVALID_PARAMETER.
//
// Set <stream name> as a random string (not an empty string or $I30) to test above requirement.
string fileName = this.ComposeRandomFileName();
string streamName = this.ComposeRandomFileName();
randomFile = fileName + ":" + streamName + ":$INDEX_ALLOCATION";
}
//Construct path name with streamTypeNameToOpen
if (streamTypeNameToOpen != StreamTypeNameToOpen.NULL &&
symbolicLinkType != SymbolicLinkType.IsSymbolicLink)
{
if (streamTypeNameToOpen == StreamTypeNameToOpen.DATA
&& !randomFile.Contains("$DATA"))
{
randomFile = randomFile + "::$DATA";
}
else if (streamTypeNameToOpen == StreamTypeNameToOpen.INDEX_ALLOCATION
&& !randomFile.Contains("$INDEX_ALLOCATION"))
{
if ((desiredFileAttribute & FileAttribute.READONLY) == FileAttribute.READONLY &&
(createOption & CreateOptions.DELETE_ON_CLOSE) == CreateOptions.DELETE_ON_CLOSE &&
(fileSystem != Adapter.FileSystem.NTFS && fileSystem != Adapter.FileSystem.REFS))
{
/*
* To cover the below requirements in a file system other than NTFS and ReFS, remove complex suffix:
* Section 2.1.5.1.1 Create a New File
* If DesiredFileAttributes.FILE_ATTRIBUTE_READONLY and CreateOptions.FILE_DELETE_ON_CLOSE are both set,
* the operation MUST be failed with STATUS_CANNOT_DELETE.
* =>
* If "::$INDEX_ALLOCATION" is added to the file name, it will return unexpected error codes in FAT32 that
* does not recognize the "::$INDEX_ALLOCATION" complex suffix.
*/
}
else
{
randomFile = randomFile + "::$INDEX_ALLOCATION";
}
}
else if (streamTypeNameToOpen == StreamTypeNameToOpen.Other
&& !randomFile.Contains("$TEST"))
{
randomFile = randomFile + "::$TEST";
}
}
MessageStatus returnedStatus = transAdapter.CreateFile(
randomFile,
(uint)desiredFileAttribute,
(uint)desiredAccess,
(uint)shareAccess,
(uint)createOption,
(uint)createDisposition,
out createAction);
gOpenGrantedAccess = returnedStatus == MessageStatus.SUCCESS ? desiredAccess : FileAccess.None;
if (this.transport == Transport.SMB2 || this.transport == Transport.SMB3)
{
returnedStatus = SMB2_TDIWorkaround.WorkaroundCreateFile(fileNameStatus, createOption, desiredAccess, openFileType, desiredFileAttribute, returnedStatus, site);
}
else
{
returnedStatus = SMB_TDIWorkaround.WorkaroundCreateFile(fileSystem, fileNameStatus, createOption, desiredAccess, openFileType, desiredFileAttribute, returnedStatus, site);
}
/*
* Work around for test cases only designed for NTFS and ReFS:
* Make assertion in the adapter, then convert the return code according to the test case.
*/
if (this.fileSystem != Adapter.FileSystem.NTFS && this.fileSystem != Adapter.FileSystem.REFS)
{
if ((createOption & CreateOptions.DIRECTORY_FILE) == CreateOptions.DIRECTORY_FILE &&
(createOption & CreateOptions.NON_DIRECTORY_FILE) == CreateOptions.NON_DIRECTORY_FILE)
{
/*
* To cover the below requirements in a file system other than NTFS and ReFS, remove complex suffix:
* Section 2.1.5.1
* Phase 1 - Parameter Validation
* If CreateOptions.FILE_DIRECTORY_FILE && CreateOptions.FILE_NON_DIRECTORY_FILE,
* the operation MUST be failed with STATUS_INVALID_PARAMETER.
* =>
* Return the status immediately.
*
* Test Cases:
* CreateFileTestCaseS12
*/
return returnedStatus;
}
else if (openFileType == FileType.DataFile && symbolicLinkType == SymbolicLinkType.IsSymbolicLink)
{
/*
* To cover the below requirements in a file system other than NTFS and ReFS that does not support Symbolic Link:
* Section 2.1.5.1
* Phase 6 -- Location of file:
* If Link.File.IsSymbolicLink is TRUE, the operation MUST be failed with Status set to STATUS_STOPPED_ON_SYMLINK
* and ReparsePointData set to Link.File.ReparsePointData.
* Section 2.1.5.1.2
* Else if FileTypeToOpen is DataFile:
* If CreateDisposition is FILE_CREATE, then the operation MUST be failed with STATUS_OBJECT_NAME_COLLISION.
* =>
* In NTFS and ReFS, these cases are expecting STATUS_STOPPED_ON_SYMLINK, while in other file system that does
* not support symbolic link will consider this action as creating a file that is already existed. The return
* will be STATUS_OBJECT_NAME_COLLISION instead.
*
* Test Cases:
* CreateFileTestCaseS42
*/
site.Log.Add(LogEntryKind.Checkpoint, @"Section 2.1.5.1.2
Else if FileTypeToOpen is DataFile:
If CreateDisposition is FILE_CREATE, then the operation MUST be failed with STATUS_OBJECT_NAME_COLLISION.");
site.Assert.AreEqual<MessageStatus>(MessageStatus.OBJECT_NAME_COLLISION, returnedStatus, "return of CreateFile");
// Make a fake return
return MessageStatus.STOPPED_ON_SYMLINK;
}
else if (randomFile.Contains("$STANDARD_INFORMATION")
|| randomFile.Contains("$ATTRIBUTE_LIST")
|| randomFile.Contains("$FILE_NAME")
|| randomFile.Contains("$OBJECT_ID")
|| randomFile.Contains("$SECURITY_DESCRIPTOR")
|| randomFile.Contains("$VOLUME_NAME")
|| randomFile.Contains("$VOLUME_INFORMATION")
|| randomFile.Contains("$DATA")
|| randomFile.Contains("$INDEX_ROOT")
|| randomFile.Contains("$INDEX_ALLOCATION")
|| randomFile.Contains("$BITMAP")
|| randomFile.Contains("$REPARSE_POINT")
|| randomFile.Contains("$EA_INFORMATION")
|| randomFile.Contains("$EA")
|| randomFile.Contains("$LOGGED_UTILITY_STREAM"))
{
if (fileNameStatus == FileNameStatus.StreamTypeNameIsINDEX_ALLOCATION)
{
return MessageStatus.INVALID_PARAMETER;
}
else if (createDisposition == CreateDisposition.OPEN
|| createDisposition == CreateDisposition.OVERWRITE
|| createDisposition == CreateDisposition.OPEN_IF)
{
/*
* To cover the below requirements in a file system other than NTFS and ReFS that does not support stream type names:
* Section 2.1.5.1
* Phase 6 -- Location of file:
* If StreamTypeNameToOpen is non-empty and StreamTypeNameToOpen is not equal to one of the stream type names
* recognized by the object store<42> (using case-insensitive string comparisons), the operation MUST be failed
* with STATUS_OBJECT_NAME_INVALID.
*
* Section 5
* <42> Section 2.1.5.1: NTFS and ReFS recognize the following stream type names:
* "$STANDARD_INFORMATION"
* "$ATTRIBUTE_LIST"
* "$FILE_NAME"
* "$OBJECT_ID"
* "$SECURITY_DESCRIPTOR"
* "$VOLUME_NAME"
* "$VOLUME_INFORMATION"
* "$DATA"
* "$INDEX_ROOT"
* "$INDEX_ALLOCATION"
* "$BITMAP"
* "$REPARSE_POINT"
* "$EA_INFORMATION"
* "$EA"
* "$LOGGED_UTILITY_STREAM"
* Other Windows file systems do not recognize any stream type names.
* =>
* In NTFS and ReFS, these cases are expecting STATUS_OBJECT_NAME_NOT_FOUND, while in other file system that does not
* support the the stream type names will return STATUS_OBJECT_NAME_INVALID.
*
* Test Cases:
* CreateFileTestCaseS38
* CreateFileTestCaseS40
* LockAndUnlockTestCaseS2
*/
site.Log.Add(LogEntryKind.Checkpoint, @"Section 2.1.5.1
Phase 6 -- Location of file:
If StreamTypeNameToOpen is non-empty and StreamTypeNameToOpen is not equal to one of the stream type names
recognized by the object store<42> (using case-insensitive string comparisons), the operation MUST be failed
with STATUS_OBJECT_NAME_INVALID.");
site.Assert.AreEqual<MessageStatus>(MessageStatus.OBJECT_NAME_INVALID, returnedStatus, "return of CreateFile");
// Remove the complex name suffixes and try to create the file again.
randomFile = randomFile.Remove(randomFile.IndexOf(":"));
returnedStatus = transAdapter.CreateFile(
randomFile,
(uint)desiredFileAttribute,
(uint)desiredAccess,
(uint)shareAccess,
(uint)createOption,
(uint)createDisposition,
out createAction);
return returnedStatus;
}
}
else if (randomFile.Contains(":$I30")
|| randomFile.Contains("::$INDEX_ALLOCATION")
|| randomFile.Contains(":$I30:$INDEX_ALLOCATION")
|| randomFile.Contains("::$BITMAP")
|| randomFile.Contains(":$I30:$BITMAP")
|| randomFile.Contains("::$ATTRIBUTE_LIST")
|| randomFile.Contains("::$REPARSE_POINT"))
{
/*
* To cover the below requirements in a file system other than NTFS and ReFS that does not complex name suffixes:
* Section 2.1.5.1
* Phase 6 -- Location of file:
* If ComplexNameSuffix is non-empty and ComplexNameSuffix is not equal to one of the complex name suffixes
* recognized by the object store<41> (using case-insensitive string comparisons), the operation MUST be
* failed with STATUS_OBJECT_NAME_INVALID.
*
* Section 5
* <41> Section 2.1.5.1: NTFS and ReFS recognize the following complex name suffixes:
* ":$I30"
* "::$INDEX_ALLOCATION"
* ":$I30:$INDEX_ALLOCATION"
* "::$BITMAP"
* ":$I30:$BITMAP"
* "::$ATTRIBUTE_LIST"
* "::$REPARSE_POINT"
* Other Windows file systems do not recognize any complex name suffixes.
* =>
* In NTFS and ReFS, these cases are expecting STATUS_SUCCESS, while in other file system that does not
* support the complex name suffixes will return STATUS_OBJECT_NAME_INVALID.
*
* Test Cases:
* ChangeNotificationTestCaseS0
* ChangeNotificationTestCaseS2
*/
site.Log.Add(LogEntryKind.Checkpoint, @"Section 2.1.5.1
Phase 6 -- Location of file:
If ComplexNameSuffix is non-empty and ComplexNameSuffix is not equal to one of the complex name suffixes
recognized by the object store<41> (using case-insensitive string comparisons), the operation MUST be
failed with STATUS_OBJECT_NAME_INVALID.");
site.Assert.AreEqual<MessageStatus>(MessageStatus.OBJECT_NAME_INVALID, returnedStatus, "return of CreateFile");
// Remove the complex name suffixes and try to create the file again.
randomFile = randomFile.Remove(randomFile.IndexOf(":"));
returnedStatus = transAdapter.CreateFile(
randomFile,
(uint)desiredFileAttribute,
(uint)desiredAccess,
(uint)shareAccess,
(uint)createOption,
(uint)createDisposition,
out createAction);
return returnedStatus;
}
}
return returnedStatus;
}