2.1.5.1 Server Requests an Open of a File

The server provides:

  • RootOpen: An Open to the root of the share.

  • PathName: A Unicode path relative to RootOpen for the file to be opened in the format specified in [MS-FSCC] section 2.1.5.

  • SecurityContext: The SecurityContext of the user performing the open.

  • DesiredAccess: A bitmask indicating requested access for the open, as specified in [MS-SMB2] section 2.2.13.1.

  • ShareAccess: A bitmask indicating sharing access for the open, as specified in [MS-SMB2] section 2.2.13.

  • CreateOptions: A bitmask of options for the open, as specified in [MS-SMB2] section 2.2.13.

  • CreateDisposition: The requested disposition for the open, as specified in [MS-SMB2] section 2.2.13.

  • DesiredFileAttributes: A bitmask of requested file attributes for the open, as specified in [MS-SMB2] section 2.2.13.

  • IsCaseInsensitive: A Boolean value. TRUE indicates that string comparisons performed in the context of this Open are case-insensitive; otherwise, they are case-sensitive.

  • TargetOplockKey: A GUID value. This value could be empty.

  • UserCertificate: An ENCRYPTION_CERTIFICATE structure as specified in [MS-EFSR] section 2.2.8 and used when opening an encrypted stream. This value could be empty.

On completion, the object store MUST return:

  • Status: An NTSTATUS code that specifies the result.

On success it MUST also return:

  • CreateAction: A code defining the action taken by the open operation, as specified in [MS-SMB2] section 2.2.14 for the CreateAction field.

  • Open: The newly created Open.

On STATUS_REPARSE or STATUS_STOPPED_ON_SYMLINK it MUST also return:

  • ReparseData: The reparse point data associated with an existing file, in the format described in [MS-FSCC] section 2.1.2. The application MAY retry the open operation with a different PathName parameter constructed using ReparseData.

Pseudocode for the operation is as follows:

  • Phase 1 -- Parameter Validation:

  • Set ValidDirectoryCreateOptions = (FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_WRITE_THROUGH | FILE_OPEN_REMOTE_INSTANCE | FILE_COMPLETE_IF_OPLOCKED | FILE_OPEN_FOR_BACKUP_INTENT | FILE_DELETE_ON_CLOSE | FILE_OPEN_FOR_FREE_SPACE_QUERY | FILE_OPEN_BY_FILE_ID | FILE_NO_COMPRESSION | FILE_OPEN_REPARSE_POINT | FILE_OPEN_REQUIRING_OPLOCK).

  • The operation MUST be failed with STATUS_INVALID_PARAMETER under any of the following conditions:

    • If RootOpen.File.FileType is DataFile.

    • If ShareAccess, CreateOptions, CreateDisposition, or FileAttributes are not valid values for a file object as specified in [MS-SMB2] section 2.2.13.

    • If (CreateOptions.FILE_SYNCHRONOUS_IO_ALERT || Create.FILE_SYNCHRONOUS_IO_NONALERT) && !DesiredAccess.SYNCHRONIZE.

    • If CreateOptions.FILE_DELETE_ON_CLOSE && !DesiredAccess.DELETE.

    • If CreateOptions.FILE_SYNCHRONOUS_IO_ALERT && Create.FILE_SYNCHRONOUS_IO_NONALERT.

    • If CreateOptions.FILE_DIRECTORY_FILE is TRUE && CreateOptions.FILE_NON_DIRECTORY_FILE is FALSE && ((CreateOptions & ~ ValidDirectoryCreateOptions) || (CreateDisposition != FILE_CREATE && CreateDisposition != FILE_OPEN && CreateDisposition != FILE_OPEN_IF)).

    • If CreateOptions.FILE_COMPLETE_IF_OPLOCKED && CreateOptions.FILE_RESERVE_OPFILTER.

    • If CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING && DesiredAccess.FILE_APPEND_DATA.

  • If DesiredAccess is zero, or if any of the bits in the mask 0x0CE0FE00 are set, the operation MUST be failed with STATUS_ACCESS_DENIED.

  • If CreateOptions.FILE_DIRECTORY_FILE && CreateOptions.FILE_NON_DIRECTORY_FILE, the operation MUST be failed with STATUS_INVALID_PARAMETER.

  • The operation MUST be failed with STATUS_OBJECT_NAME_INVALID under any of the following conditions:

    • If PathName is not valid as specified in [MS-FSCC] section 2.1.5.

    • If PathName contains a trailing backslash and CreateOptions.FILE_NON_DIRECTORY_FILE is TRUE.

  • If DesiredFileAttributes.FILE_ATTRIBUTE_ENCRYPTED is specified, then the object store MUST set CreateOptions.FILE_NO_COMPRESSION.

  • Phase 2 -- Volume State:

  • If RootOpen.File.Volume.IsReadOnly && (CreateDisposition == FILE_CREATE || CreateDisposition == FILE_SUPERSEDE || CreateDisposition == OVERWRITE || CreateDisposition == OVERWRITE_IF) then the operation MUST be failed with STATUS_MEDIA_WRITE_PROTECTED.

  • Phase 3 -- Initialization of Open Object:

  • The object store MUST build a new Open object with fields initialized as follows:

    • Open.RootOpen set to RootOpen.

    • Open.FileName formed by concatenating RootOpen.FileName + "\" + FileName, stripping any redundant backslashes and trailing backslashes.

    • Open.RemainingDesiredAccess set to DesiredAccess.

    • Open.SharingMode set to ShareAccess.

    • Open.Mode set to (CreateOptions & (FILE_WRITE_THROUGH | FILE_SEQUENTIAL_ONLY | FILE_NO_INTERMEDIATE_BUFFERING | FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT | FILE_DELETE_ON_CLOSE)).

    • Open.IsCaseInsensitive set to IsCaseInsensitive.

    • Open.HasBackupAccess set to TRUE if SecurityContext.PrivilegeSet contains "SeBackupPrivilege".

    • Open.HasRestoreAccess set to TRUE if SecurityContext.PrivilegeSet contains "SeRestorePrivilege".

    • Open.HasCreateSymbolicLinkAccess set to TRUE if SecurityContext.PrivilegeSet contains "SeCreateSymbolicLinkPrivilege".

    • Open.HasManageVolumeAccess set to TRUE if SecurityContext.PrivilegeSet contains "SeManageVolumePrivilege".

    • Open.IsAdministrator set to TRUE if SecurityContext.SIDs contains the well-known SID BUILTIN_ADMINISTRATORS as defined in [MS-DTYP] section 2.4.2.4.

    • Open.TargetOplockKey set to TargetOplockKey.

    • Open.LastQuotaId set to -1.

    • All other fields set to zero.

  • Phase 4 -- Check for backup/restore intent

  • If CreateOptions.FILE_OPEN_FOR_BACKUP_INTENT is set and (CreateDisposition == FILE_OPEN || CreateDisposition == FILE_OPEN_IF || CreateDisposition == FILE_OVERWRITE_IF) and Open.HasBackupAccess is TRUE, then the object store SHOULD grant backup access as shown in the following pseudocode:

    • BackupAccess = (READ_CONTROL | ACCESS_SYSTEM_SECURITY | FILE_GENERIC_READ | FILE_TRAVERSE)

    • If Open.RemainingDesiredAccess.MAXIMUM_ALLOWED is set then:

      • Open.GrantedAccess |= BackupAccess

    • Else:

      • Open.GrantedAccess |= (Open.RemainingDesiredAccess & BackupAccess)

    • EndIf

    • Open.RemainingDesiredAccess &= ~Open.GrantedAccess

  • If CreateOptions.FILE_OPEN_FOR_BACKUP_INTENT is set and Open.HasRestoreAccess is TRUE, then the object store SHOULD grant restore access as shown in the following pseudocode:

    • RestoreAccess = (WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY | FILE_GENERIC_WRITE | FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY | DELETE)

    • If Open.RemainingDesiredAccess.MAXIMUM_ALLOWED is set then:

      • Open.GrantedAccess |= RestoreAccess

    • Else:

      • Open.GrantedAccess |= (Open.RemainingDesiredAccess & RestoreAccess)

    • EndIf

    • Open.RemainingDesiredAccess &= ~Open.GrantedAccess

  • Phase 5 -- Parse pathname:

  • The object store MUST split Open.FileName into pathname components PathName1 ... PathNamen, using the backslash ("\") character as a delimiter. If any PathNamei ends in a colon(":") character, the operation MUST be failed with STATUS_OBJECT_NAME_INVALID. The object store MUST further split each PathNamei into a file name component FileNamei, stream name component StreamNamei, and stream type name component StreamTypeNamei, using the colon (":") character as a delimiter (FileNamei:StreamNamei:StreamTypeNamei). If StreamNamei or StreamTypeNamei is not present in the name, the value MUST be set to an empty string.

  • Phase 6 -- Location of file:

  • The object store MUST search for a filename matching Open.FileName. If IsCaseInsensitive is TRUE, then the search MUST be case-insensitive; otherwise it MUST be case-sensitive. Pseudocode for this search is as follows:

    • Set ParentFile = RootOpen.File.

    • // Examine each prefix pathname component in order.

    • For i = 1 to n-1: // n is the number of pathname components, from Phase 5.

      • If StreamTypeNamei is non-empty:

        • Set ComplexNameSuffix = ":StreamNamei:StreamTypeNamei".

      • Else if StreamNamei is non-empty:

        • Set ComplexNameSuffix = ":StreamNamei".

      • Else:

        • Set ComplexNameSuffix to empty.

      • EndIf

      • If ComplexNameSuffix is non-empty and ComplexNameSuffix is not equal to one of the complex name suffixes recognized by the object store<45> (using case-insensitive string comparisons), the operation MUST be failed with STATUS_OBJECT_NAME_INVALID.

      • Search ParentFile.DirectoryList for a Link where Link.Name or Link.ShortName matches FileNamei. If no such link is found, the operation MUST be failed with STATUS_OBJECT_PATH_NOT_FOUND.

      • If Link.File.FileType is not DirectoryFile, the operation MUST be failed with STATUS_OBJECT_PATH_NOT_FOUND.

      • If Open.GrantedAccess.FILE_TRAVERSE is not set and AccessCheck(SecurityContext, Link.File.SecurityDescriptor, FILE_TRAVERSE) returns FALSE, the operation MAY be failed with STATUS_ACCESS_DENIED.

      • If Link.IsDeleted, the operation MUST be failed with STATUS_DELETE_PENDING.

      • If Link.File.IsSymbolicLink is TRUE, the operation MUST be failed with Status set to STATUS_STOPPED_ON_SYMLINK and ReparseData set to Link.File.ReparseData.

      • Set ParentFile = Link.File.

    • EndFor

    • // Examine final pathname component.

    • Set FileNameToOpen to FileNamen, StreamNameToOpen to StreamNamen, and StreamTypeNameToOpen to StreamTypeNamen.

    • If StreamTypeNameToOpen is non-empty and StreamTypeNameToOpen is not equal to one of the stream type names recognized by the object store<46> (using case-insensitive string comparisons), the operation MUST be failed with STATUS_OBJECT_NAME_INVALID.

    • Search ParentFile.DirectoryList for a Link where Link.Name or Link.ShortName matches FileNameToOpen. If such a link is found:

      • Set File = Link.File.

      • Set Open.File to File.

      • Set Open.Link to Link.

    • Else:

      • If (CreateDisposition == FILE_OPEN || CreateDisposition == FILE_OVERWRITE), the operation MUST be failed with STATUS_OBJECT_NAME_NOT_FOUND.

      • If RootOpen.File.Volume.IsReadOnly then the operation MUST be failed with STATUS_MEDIA_WRITE_PROTECTED.

    • EndIf

    • If StreamTypeNameToOpen is non-empty and has a value other than "$DATA" or "$INDEX_ALLOCATION", the operation MUST be failed with STATUS_OBJECT_NAME_INVALID.

  • Phase 7 -- Type of stream to open:

  • The object store MUST use the following algorithm to determine which type of stream is being opened:

  • Set StreamTypeToOpen to empty.

  • If RootOpen.File.Volume.IsPhysicalRoot is TRUE, then set StreamTypeToOpen to ViewIndexStream under any of the following conditions:

    • If RootOpen.File.Volume.IsObjectIDsSupported is TRUE, BuildRelativeName(Open.Link, Open.File.Volume.RootDirectory) is equal to "\$Extend\$ObjId", StreamNameToOpen is equal to "$O", and StreamTypeNameToOpen is equal to "$INDEX_ALLOCATION" (using case-insensitive string comparisons).

    • If RootOpen.File.Volume.IsQuotasSupported is TRUE, BuildRelativeName(Open.Link, Open.File.Volume.RootDirectory) is equal to "\$Extend\$Quota", StreamNameToOpen is equal to "$O" or "$Q", and StreamTypeNameToOpen is equal to "$INDEX_ALLOCATION" (using case-insensitive string comparisons).

    • If RootOpen.File.Volume.IsReparsePointsSupported is TRUE, BuildRelativeName(Open.Link, Open.File.Volume.RootDirectory) is equal to "\$Extend\$Reparse", StreamNameToOpen is equal to "$R", and StreamTypeNameToOpen is equal to "$INDEX_ALLOCATION" (using case-insensitive string comparisons).

  • EndIf

  • // Note that when StreamTypeToOpen is ViewIndexStream, the file always exists in the object store and

  • // Open.File.FileType is ViewIndexFile.

  • If StreamTypeToOpen is empty:

    • If StreamTypeNameToOpen is "$INDEX_ALLOCATION":

      • If StreamNameToOpen has a value other than an empty string or "$I30", the operation SHOULD<47> be failed with STATUS_INVALID_PARAMETER.

    • Else if StreamTypeNameToOpen is not "$DATA” and not empty:

      • If CreateDisposition is one of FILE_SUPERSEDE, FILE_OVERWRITE, or FILE_OVERWRITE_IF, then the operation MUST be failed with STATUS_ACCESS_DENIED.

    • EndIf

    • If CreateOptions.FILE_DIRECTORY_FILE is TRUE then StreamTypeToOpen = DirectoryStream.

    • Else if StreamTypeNameToOpen is "$INDEX_ALLOCATION" then StreamTypeToOpen = DirectoryStream.

    • Else if CreateOptions.FILE_NON_DIRECTORY_FILE is FALSE, StreamNameToOpen is empty, StreamTypeNameToOpen is empty, Open.File is not NULL, and Open.File.FileType is DirectoryFile then StreamTypeToOpen = DirectoryStream.

    • Else StreamTypeToOpen = DataStream.

    • EndIf

  • EndIf

  • If StreamTypeToOpen is DirectoryStream:

    • If StreamTypeNameToOpen is not "$INDEX_ALLOCATION":

      • If StreamNameToOpen is not empty or StreamTypeNameToOpen is not empty, then the operation MUST be failed with STATUS_NOT_A_DIRECTORY.

    • EndIf

    • If Open.File is not NULL and Open.File.FileType is DataFile:

      • If CreateDisposition == FILE_CREATE then the operation MUST be failed with STATUS_OBJECT_NAME_COLLISION, else the operation MUST be failed with STATUS_NOT_A_DIRECTORY.

    • EndIf

  • Else if StreamTypeToOpen is DataStream:

    • If StreamNameToOpen is empty and Open.File is not NULL and Open.File.FileType is DirectoryFile, the operation MUST be failed with STATUS_FILE_IS_A_DIRECTORY.

  • EndIf

  • If PathName contains a trailing backslash:

    • If StreamTypeToOpen is DataStream or CreateOptions.FILE_NON_DIRECTORY_FILE is TRUE, the operation MUST be failed with STATUS_OBJECT_NAME_INVALID.

  • EndIf

  • Phase 8 -- Completion of open

  • If Open.File is NULL, the object store MUST create a new file as described in section 2.1.5.1.1; otherwise the object store MUST open the existing file as described in section 2.1.5.1.2.