public MessageStatus CreateFile()

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;
        }