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.
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.