package smb2

import (
	
	
	
	
	
	
	
	
	
	
	

	. 
	. 

	
)

// Dialer contains options for func (*Dialer) Dial.
type Dialer struct {
	MaxCreditBalance uint16 // if it's zero, clientMaxCreditBalance is used. (See feature.go for more details)
	Negotiator       Negotiator
	Initiator        Initiator
}

// Dial performs negotiation and authentication.
// It returns a session. It doesn't support NetBIOS transport.
// This implementation doesn't support multi-session on the same TCP connection.
// If you want to use another session, you need to prepare another TCP connection at first.
func ( *Dialer) ( net.Conn) (*Session, error) {
	return .DialContext(context.Background(), )
}

// DialContext performs negotiation and authentication using the provided context.
// Note that returned session doesn't inherit context.
// If you want to use the same context, call Session.WithContext manually.
// This implementation doesn't support multi-session on the same TCP connection.
// If you want to use another session, you need to prepare another TCP connection at first.
func ( *Dialer) ( context.Context,  net.Conn) (*Session, error) {
	if  == nil {
		panic("nil context")
	}
	if .Initiator == nil {
		return nil, &InternalError{"Initiator is empty"}
	}
	if ,  := .Initiator.(*NTLMInitiator);  {
		if .User == "" {
			return nil, &InternalError{"Anonymous account is not supported yet. Use guest account instead"}
		}
	}

	 := .MaxCreditBalance
	if  == 0 {
		 = clientMaxCreditBalance
	}

	 := openAccount()

	,  := .Negotiator.negotiate(direct(), , )
	if  != nil {
		return nil, 
	}

	,  := sessionSetup(, .Initiator, )
	if  != nil {
		return nil, 
	}

	return &Session{s: , ctx: context.Background(), addr: .RemoteAddr().String()}, nil
}

// Session represents a SMB session.
type Session struct {
	s    *session
	ctx  context.Context
	addr string
}

func ( *Session) ( context.Context) *Session {
	if  == nil {
		panic("nil context")
	}
	return &Session{s: .s, ctx: , addr: .addr}
}

// Logoff invalidates the current SMB session.
func ( *Session) () error {
	return .s.logoff(.ctx)
}

// Mount mounts the SMB share.
// sharename must follow format like `<share>` or `\\<server>\<share>`.
// Note that the mounted share doesn't inherit session's context.
// If you want to use the same context, call Share.WithContext manually.
func ( *Session) ( string) (*Share, error) {
	 = normPath()

	if !strings.ContainsRune(, '\\') {
		 = fmt.Sprintf(`\\%s\%s`, .addr, )
	}

	if  := validateMountPath();  != nil {
		return nil, 
	}

	,  := treeConnect(.s, , 0, .ctx)
	if  != nil {
		return nil, 
	}

	return &Share{treeConn: , ctx: context.Background()}, nil
}

func ( *Session) () ([]string, error) {
	 := .addr

	,  := .Mount(fmt.Sprintf(`\\%s\IPC$`, ))
	if  != nil {
		return nil, 
	}
	defer .Umount()

	 = .WithContext(.ctx)

	,  := .OpenFile("srvsvc", os.O_RDWR, 0666)
	if  != nil {
		return nil, 
	}
	defer .Close()

	 := rand.Uint32()

	 := &IoctlRequest{
		CtlCode:           FSCTL_PIPE_TRANSCEIVE,
		OutputOffset:      0,
		OutputCount:       0,
		MaxInputResponse:  0,
		MaxOutputResponse: 4280,
		Flags:             SMB2_0_IOCTL_IS_FSCTL,
		Input: &msrpc.Bind{
			CallId: ,
		},
	}

	,  := .ioctl()
	if  != nil {
		return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: }
	}

	 := msrpc.BindAckDecoder()
	if .IsInvalid() || .CallId() !=  {
		return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: &InvalidResponseError{"broken bind ack response format"}}
	}

	++

	 := &IoctlRequest{
		CtlCode:          FSCTL_PIPE_TRANSCEIVE,
		OutputOffset:     0,
		OutputCount:      0,
		MaxInputResponse: 0,
		// MaxOutputResponse: 4280,
		MaxOutputResponse: 1024,
		Flags:             SMB2_0_IOCTL_IS_FSCTL,
		Input: &msrpc.NetShareEnumAllRequest{
			CallId:     ,
			ServerName: ,
			Level:      1, // level 1 seems to be portable
		},
	}

	,  = .ioctl()
	if  != nil {
		if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_BUFFER_OVERFLOW {
			 := make([]byte, 4280)

			 := 4280 - len()

			,  := .readAt([:], 0)
			if  != nil {
				return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: }
			}

			 = append(, [:]...)

			 := msrpc.NetShareEnumAllResponseDecoder()
			if .IsInvalid() || .CallId() !=  {
				return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: &InvalidResponseError{"broken net share enum response format"}}
			}

			for .IsIncomplete() {
				,  := .readAt(, 0)
				if  != nil {
					return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: }
				}

				 := msrpc.NetShareEnumAllResponseDecoder([:])
				if .IsInvalid() || .CallId() !=  {
					return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: &InvalidResponseError{"broken net share enum response format"}}
				}

				 = append(, .Buffer()...)

				 = msrpc.NetShareEnumAllResponseDecoder()
			}

			return .ShareNameList(), nil
		}

		return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: }
	}

	 := msrpc.NetShareEnumAllResponseDecoder()
	if .IsInvalid() || .IsIncomplete() || .CallId() !=  {
		return nil, &os.PathError{Op: "listSharenames", Path: .name, Err: &InvalidResponseError{"broken net share enum response format"}}
	}

	return .ShareNameList(), nil
}

// Share represents a SMB tree connection with VFS interface.
type Share struct {
	*treeConn
	ctx context.Context
}

func ( *Share) ( context.Context) *Share {
	if  == nil {
		panic("nil context")
	}
	return &Share{
		treeConn: .treeConn,
		ctx:      ,
	}
}

// Umount disconects the current SMB tree.
func ( *Share) () error {
	return .treeConn.disconnect(.ctx)
}

func ( *Share) ( string) (*File, error) {
	return .OpenFile(, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
}

func ( *Share) ( CreateResponseDecoder,  string) *File {
	 := .FileId().Decode()

	 := &FileStat{
		CreationTime:   time.Unix(0, .CreationTime().Nanoseconds()),
		LastAccessTime: time.Unix(0, .LastAccessTime().Nanoseconds()),
		LastWriteTime:  time.Unix(0, .LastWriteTime().Nanoseconds()),
		ChangeTime:     time.Unix(0, .ChangeTime().Nanoseconds()),
		EndOfFile:      .EndofFile(),
		AllocationSize: .AllocationSize(),
		FileAttributes: .FileAttributes(),
		FileName:       base(),
	}

	 := &File{fs: , fd: , name: , fileStat: }

	runtime.SetFinalizer(, (*File).close)

	return 
}

func ( *Share) ( string) (*File, error) {
	return .OpenFile(, os.O_RDONLY, 0)
}

func ( *Share) ( string,  int,  os.FileMode) (*File, error) {
	 = normPath()

	if  := validatePath("open", , false);  != nil {
		return nil, 
	}

	var  uint32
	switch  & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) {
	case os.O_RDONLY:
		 = GENERIC_READ
	case os.O_WRONLY:
		 = GENERIC_WRITE
	case os.O_RDWR:
		 = GENERIC_READ | GENERIC_WRITE
	}
	if &os.O_CREATE != 0 {
		 |= GENERIC_WRITE
	}
	if &os.O_APPEND != 0 {
		 &^= GENERIC_WRITE
		 |= FILE_APPEND_DATA
	}

	 := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)

	var  uint32
	switch {
	case &(os.O_CREATE|os.O_EXCL) == (os.O_CREATE | os.O_EXCL):
		 = FILE_CREATE
	case &(os.O_CREATE|os.O_TRUNC) == (os.O_CREATE | os.O_TRUNC):
		 = FILE_OVERWRITE_IF
	case &os.O_CREATE == os.O_CREATE:
		 = FILE_OPEN_IF
	case &os.O_TRUNC == os.O_TRUNC:
		 = FILE_OVERWRITE
	default:
		 = FILE_OPEN
	}

	var  uint32 = FILE_ATTRIBUTE_NORMAL
	if &0200 == 0 {
		 = FILE_ATTRIBUTE_READONLY
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        ,
		FileAttributes:       ,
		ShareAccess:          ,
		CreateDisposition:    ,
		CreateOptions:        FILE_SYNCHRONOUS_IO_NONALERT,
	}

	,  := .createFile(, , true)
	if  != nil {
		return nil, &os.PathError{Op: "open", Path: , Err: }
	}
	if &os.O_APPEND != 0 {
		.seek(0, io.SeekEnd)
	}
	return , nil
}

func ( *Share) ( string,  os.FileMode) error {
	 = normPath()

	if  := validatePath("mkdir", , false);  != nil {
		return 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_WRITE_ATTRIBUTES,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_CREATE,
		CreateOptions:        FILE_DIRECTORY_FILE,
	}

	,  := .createFile(, , false)
	if  != nil {
		return &os.PathError{Op: "mkdir", Path: , Err: }
	}

	 = .close()
	if  != nil {
		return &os.PathError{Op: "mkdir", Path: , Err: }
	}
	return nil
}

func ( *Share) ( string) (string, error) {
	 = normPath()

	if  := validatePath("readlink", , false);  != nil {
		return "", 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_READ_ATTRIBUTES,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        FILE_OPEN_REPARSE_POINT,
	}

	,  := .createFile(, , false)
	if  != nil {
		return "", &os.PathError{Op: "readlink", Path: , Err: }
	}

	 := &IoctlRequest{
		CtlCode:           FSCTL_GET_REPARSE_POINT,
		OutputOffset:      0,
		OutputCount:       0,
		MaxInputResponse:  0,
		MaxOutputResponse: uint32(.maxTransactSize()),
		Flags:             SMB2_0_IOCTL_IS_FSCTL,
		Input:             nil,
	}

	,  := .ioctl()
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return "", &os.PathError{Op: "readlink", Path: .name, Err: }
	}

	 := SymbolicLinkReparseDataBufferDecoder()
	if .IsInvalid() {
		return "", &os.PathError{Op: "readlink", Path: .name, Err: &InvalidResponseError{"broken symbolic link response data buffer format"}}
	}

	 := .SubstituteName()

	switch {
	case strings.HasPrefix(, `\??\UNC\`):
		 = `\\` + [8:]
	case strings.HasPrefix(, `\??\`):
		 = [4:]
	}

	return , nil
}

func ( *Share) ( string) error {
	 := .remove()
	if os.IsPermission() {
		if  := .Chmod(, 0666);  != nil {
			return 
		}
		return .remove()
	}
	return 
}

func ( *Share) ( string) error {
	 = normPath()

	if  := validatePath("remove", , false);  != nil {
		return 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        DELETE,
		FileAttributes:       0,
		ShareAccess:          FILE_SHARE_DELETE,
		CreateDisposition:    FILE_OPEN,
		// CreateOptions:        FILE_OPEN_REPARSE_POINT | FILE_DELETE_ON_CLOSE,
		CreateOptions: FILE_OPEN_REPARSE_POINT,
	}
	// FILE_DELETE_ON_CLOSE doesn't work for reparse point, so use FileDispositionInformation instead

	,  := .createFile(, , false)
	if  != nil {
		return &os.PathError{Op: "remove", Path: , Err: }
	}

	 = .remove()
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return &os.PathError{Op: "remove", Path: , Err: }
	}

	return nil
}

func ( *Share) (,  string) error {
	 = normPath()
	 = normPath()

	if  := validatePath("rename from", , false);  != nil {
		return 
	}

	if  := validatePath("rename to", , false);  != nil {
		return 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        DELETE,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_DELETE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        FILE_OPEN_REPARSE_POINT,
	}

	,  := .createFile(, , false)
	if  != nil {
		return &os.LinkError{Op: "rename", Old: , New: , Err: }
	}

	 := &SetInfoRequest{
		FileInfoClass:         FileRenameInformation,
		AdditionalInformation: 0,
		Input: &FileRenameInformationType2Encoder{
			ReplaceIfExists: 0,
			RootDirectory:   0,
			FileName:        ,
		},
	}

	 = .setInfo()
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return &os.LinkError{Op: "rename", Old: , New: , Err: }
	}
	return nil
}

// Symlink mimics os.Symlink.
// This API should work on latest Windows and latest MacOS.
// However it may not work on Linux because Samba doesn't support reparse point well.
// Also there is a restriction on target pathname.
// Generally, a pathname begins with leading backslash (e.g `\dir\name`) can be interpreted as two ways.
// On windows, it is evaluated as a relative path, on other systems, it is evaluated as an absolute path.
// This implementation always assumes that format is absolute path.
// So, if you know the target server is Windows, you should avoid that format.
// If you want to use an absolute target path on windows, you can use // `C:\dir\name` format instead.
func ( *Share) (,  string) error {
	 = normPath()
	 = normPath()

	if  := validatePath("symlink target", , true);  != nil {
		return 
	}

	if  := validatePath("symlink linkpath", , false);  != nil {
		return 
	}

	 := new(SymbolicLinkReparseDataBuffer)

	if len() >= 2 && [1] == ':' {
		if len() == 2 {
			return os.ErrInvalid
		}

		if [2] != '\\' {
			.Flags = SYMLINK_FLAG_RELATIVE
		}
		.SubstituteName = `\??\` + 
		.PrintName = .SubstituteName[4:]
	} else {
		if [0] != '\\' {
			.Flags = SYMLINK_FLAG_RELATIVE // It's not true on window server.
		}
		.SubstituteName = 
		.PrintName = .SubstituteName
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_WRITE_ATTRIBUTES | DELETE,
		FileAttributes:       FILE_ATTRIBUTE_REPARSE_POINT,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_CREATE,
		CreateOptions:        FILE_OPEN_REPARSE_POINT,
	}

	,  := .createFile(, , false)
	if  != nil {
		return &os.LinkError{Op: "symlink", Old: , New: , Err: }
	}

	 := &IoctlRequest{
		CtlCode:           FSCTL_SET_REPARSE_POINT,
		OutputOffset:      0,
		OutputCount:       0,
		MaxInputResponse:  0,
		MaxOutputResponse: 0,
		Flags:             SMB2_0_IOCTL_IS_FSCTL,
		Input:             ,
	}

	_,  = .ioctl()
	if  != nil {
		.remove()
		.close()

		return &os.PathError{Op: "symlink", Path: .name, Err: }
	}

	 = .close()
	if  != nil {
		return &os.PathError{Op: "symlink", Path: .name, Err: }
	}

	return nil
}

func ( *Share) ( string) (os.FileInfo, error) {
	 = normPath()

	if  := validatePath("lstat", , false);  != nil {
		return nil, 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_READ_ATTRIBUTES,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        FILE_OPEN_REPARSE_POINT,
	}

	,  := .createFile(, , false)
	if  != nil {
		return nil, &os.PathError{Op: "stat", Path: , Err: }
	}

	,  := .fileStat, nil
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return nil, &os.PathError{Op: "stat", Path: , Err: }
	}
	return , nil
}

func ( *Share) ( string) (os.FileInfo, error) {
	 = normPath()

	if  := validatePath("stat", , false);  != nil {
		return nil, 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_READ_ATTRIBUTES,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        0,
	}

	,  := .createFile(, , true)
	if  != nil {
		return nil, &os.PathError{Op: "stat", Path: , Err: }
	}

	,  := .fileStat, nil
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return nil, &os.PathError{Op: "stat", Path: , Err: }
	}
	return , nil
}

func ( *Share) ( string,  int64) error {
	 = normPath()

	if  := validatePath("truncate", , false);  != nil {
		return 
	}

	if  < 0 {
		return os.ErrInvalid
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_WRITE_DATA,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
	}

	,  := .createFile(, , true)
	if  != nil {
		return &os.PathError{Op: "truncate", Path: , Err: }
	}

	 = .truncate()
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return &os.PathError{Op: "truncate", Path: , Err: }
	}
	return nil
}

func ( *Share) ( string,  time.Time,  time.Time) error {
	 = normPath()

	if  := validatePath("chtimes", , false);  != nil {
		return 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_WRITE_ATTRIBUTES,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        0,
	}

	,  := .createFile(, , true)
	if  != nil {
		return &os.PathError{Op: "chtimes", Path: , Err: }
	}

	 := &SetInfoRequest{
		FileInfoClass:         FileBasicInformation,
		AdditionalInformation: 0,
		Input: &FileBasicInformationEncoder{
			LastAccessTime: NsecToFiletime(.UnixNano()),
			LastWriteTime:  NsecToFiletime(.UnixNano()),
		},
	}

	 = .setInfo()
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return &os.PathError{Op: "chtimes", Path: , Err: }
	}
	return nil
}

func ( *Share) ( string,  os.FileMode) error {
	 = normPath()

	if  := validatePath("chmod", , false);  != nil {
		return 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        0,
	}

	,  := .createFile(, , true)
	if  != nil {
		return &os.PathError{Op: "chmod", Path: , Err: }
	}

	 = .chmod()
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return &os.PathError{Op: "chmod", Path: , Err: }
	}
	return nil
}

func ( *Share) ( string) ([]os.FileInfo, error) {
	,  := .Open()
	if  != nil {
		return nil, 
	}
	defer .Close()

	,  := .Readdir(-1)
	if  != nil {
		return nil, 
	}

	sort.Slice(, func(,  int) bool { return [].Name() < [].Name() })

	return , nil
}

const (
	intSize = 32 << (^uint(0) >> 63) // 32 or 64
	maxInt  = 1<<(intSize-1) - 1
)

func ( *Share) ( string) ([]byte, error) {
	,  := .Open()
	if  != nil {
		return nil, 
	}
	defer .Close()

	 := .fileStat.Size() + 1 // one byte for final read at EOF

	var  int

	if  <= maxInt {
		 = int()

		// If a file claims a small size, read at least 512 bytes.
		// In particular, files in Linux's /proc claim size 0 but
		// then do not work right if read in small pieces,
		// so an initial read of 1 byte would not work correctly.
		if  < 512 {
			 = 512
		}
	} else {
		 = maxInt
	}

	 := make([]byte, 0, )
	for {
		if len() >= cap() {
			 := append([:cap()], 0)
			 = [:len()]
		}
		,  := .Read([len():cap()])
		 = [:len()+]
		if  != nil {
			if  == io.EOF {
				 = nil
			}
			return , 
		}
	}
}

func ( *Share) ( string,  []byte,  os.FileMode) error {
	,  := .OpenFile(, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, )
	if  != nil {
		return 
	}

	_,  = .Write()
	if  := .Close();  == nil {
		 = 
	}

	return 
}

func ( *Share) ( string) (FileFsInfo, error) {
	 = normPath()

	if  := validatePath("statfs", , false);  != nil {
		return nil, 
	}

	 := &CreateRequest{
		SecurityFlags:        0,
		RequestedOplockLevel: SMB2_OPLOCK_LEVEL_NONE,
		ImpersonationLevel:   Impersonation,
		SmbCreateFlags:       0,
		DesiredAccess:        FILE_READ_ATTRIBUTES,
		FileAttributes:       FILE_ATTRIBUTE_NORMAL,
		ShareAccess:          FILE_SHARE_READ | FILE_SHARE_WRITE,
		CreateDisposition:    FILE_OPEN,
		CreateOptions:        FILE_DIRECTORY_FILE,
	}

	,  := .createFile(, , true)
	if  != nil {
		return nil, &os.PathError{Op: "statfs", Path: , Err: }
	}

	,  := .statfs()
	if  := .close();  == nil {
		 = 
	}
	if  != nil {
		return nil, &os.PathError{Op: "statfs", Path: , Err: }
	}
	return , nil
}

func ( *Share) ( string,  *CreateRequest,  bool) ( *File,  error) {
	if  {
		return .createFileRec(, )
	}

	.CreditCharge, _,  = .loanCredit(0)
	defer func() {
		if  != nil {
			.chargeCredit(.CreditCharge)
		}
	}()
	if  != nil {
		return nil, 
	}

	.Name = 

	,  := .sendRecv(SMB2_CREATE, )
	if  != nil {
		return nil, 
	}

	 := CreateResponseDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken create response format"}
	}

	 = .newFile(, )

	return , nil
}

func ( *Share) ( string,  *CreateRequest) ( *File,  error) {
	for  := 0;  < clientMaxSymlinkDepth; ++ {
		.CreditCharge, _,  = .loanCredit(0)
		defer func() {
			if  != nil {
				.chargeCredit(.CreditCharge)
			}
		}()
		if  != nil {
			return nil, 
		}

		.Name = 

		,  := .sendRecv(SMB2_CREATE, )
		if  != nil {
			if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_STOPPED_ON_SYMLINK {
				if len(.data) > 0 {
					,  = evalSymlinkError(.Name, .data[0])
					if  != nil {
						return nil, 
					}
					continue
				}
			}
			return nil, 
		}

		 := CreateResponseDecoder()
		if .IsInvalid() {
			return nil, &InvalidResponseError{"broken create response format"}
		}

		 = .newFile(, )

		return , nil
	}

	return nil, &InternalError{"Too many levels of symbolic links"}
}

func evalSymlinkError( string,  []byte) (string, error) {
	 := SymbolicLinkErrorResponseDecoder()
	if .IsInvalid() {
		return "", &InvalidResponseError{"broken symbolic link error response format"}
	}

	,  := .SplitUnparsedPath()
	if  == "" &&  == "" {
		return "", &InvalidResponseError{"broken symbolic link error response format"}
	}

	 := .SubstituteName()

	switch {
	case strings.HasPrefix(, `\??\UNC\`):
		 = `\\` + [8:]
	case strings.HasPrefix(, `\??\`):
		 = [4:]
	}

	if .Flags()&SYMLINK_FLAG_RELATIVE == 0 {
		return  + , nil
	}

	return dir() +  + , nil
}

func ( *Share) ( uint16,  Packet) ( []byte,  error) {
	,  := .send(, .ctx)
	if  != nil {
		return nil, 
	}

	,  := .recv()
	if  != nil {
		return nil, 
	}

	return accept(, )
}

func ( *Share) ( int) ( uint16,  int,  error) {
	return .session.conn.loanCredit(, .ctx)
}

type File struct {
	fs          *Share
	fd          *FileId
	name        string
	fileStat    *FileStat
	dirents     []os.FileInfo
	noMoreFiles bool

	offset int64

	m sync.Mutex
}

func ( *File) () error {
	if  == nil {
		return os.ErrInvalid
	}

	 := .close()
	if  != nil {
		return &os.PathError{Op: "close", Path: .name, Err: }
	}
	return nil
}

func ( *File) () error {
	if  == nil || .fd == nil {
		return os.ErrInvalid
	}

	 := &CloseRequest{
		Flags: 0,
	}

	.CreditCharge = 1

	.FileId = .fd

	,  := .sendRecv(SMB2_CLOSE, )
	if  != nil {
		return 
	}

	 := CloseResponseDecoder()
	if .IsInvalid() {
		return &InvalidResponseError{"broken close response format"}
	}

	.fd = nil

	runtime.SetFinalizer(, nil)

	return nil
}

func ( *File) () error {
	 := &SetInfoRequest{
		FileInfoClass:         FileDispositionInformation,
		AdditionalInformation: 0,
		Input: &FileDispositionInformationEncoder{
			DeletePending: 1,
		},
	}

	 := .setInfo()
	if  != nil {
		return 
	}
	return nil
}

func ( *File) () string {
	return .name
}

func ( *File) ( []byte) ( int,  error) {
	.m.Lock()
	defer .m.Unlock()

	,  := .seek(0, io.SeekCurrent)
	if  != nil {
		return -1, &os.PathError{Op: "read", Path: .name, Err: }
	}

	,  = .readAt(, )
	if  != 0 {
		if ,  := .seek(+int64(), io.SeekStart);  == nil {
			 = 
		}
	}
	if  != nil {
		if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_END_OF_FILE {
			return , io.EOF
		}
		return , &os.PathError{Op: "read", Path: .name, Err: }
	}

	return
}

// ReadAt implements io.ReaderAt.
func ( *File) ( []byte,  int64) ( int,  error) {
	if  < 0 {
		return -1, os.ErrInvalid
	}

	,  = .readAt(, )
	if  != nil {
		if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_END_OF_FILE {
			return , io.EOF
		}
		return , &os.PathError{Op: "read", Path: .name, Err: }
	}
	return , nil
}

const winMaxPayloadSize = 1024 * 1024 // windows system don't accept more than 1M bytes request even though they tell us maxXXXSize > 1M
const singleCreditMaxPayloadSize = 64 * 1024

func ( *File) () int {
	 := int(.fs.maxReadSize)
	if  > winMaxPayloadSize {
		 = winMaxPayloadSize
	}
	if .fs.conn.capabilities&SMB2_GLOBAL_CAP_LARGE_MTU == 0 {
		if  > singleCreditMaxPayloadSize {
			 = singleCreditMaxPayloadSize
		}
	}
	return 
}

func ( *File) () int {
	 := int(.fs.maxWriteSize)
	if  > winMaxPayloadSize {
		 = winMaxPayloadSize
	}
	if .fs.conn.capabilities&SMB2_GLOBAL_CAP_LARGE_MTU == 0 {
		if  > singleCreditMaxPayloadSize {
			 = singleCreditMaxPayloadSize
		}
	}
	return 
}

func ( *File) () int {
	 := int(.fs.maxTransactSize)
	if  > winMaxPayloadSize {
		 = winMaxPayloadSize
	}
	if .fs.conn.capabilities&SMB2_GLOBAL_CAP_LARGE_MTU == 0 {
		if  > singleCreditMaxPayloadSize {
			 = singleCreditMaxPayloadSize
		}
	}
	return 
}

func ( *File) ( []byte,  int64) ( int,  error) {
	if  < 0 {
		return -1, os.ErrInvalid
	}

	 := .maxReadSize()

	for {
		switch {
		case len()- == 0:
			return , nil
		case len()- <= :
			, ,  := .readAtChunk(len()-, int64()+)
			if  != nil {
				if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_END_OF_FILE &&  != 0 {
					return , nil
				}
				return 0, 
			}

			 += copy([:], )

			if  {
				return , nil
			}
		default:
			, ,  := .readAtChunk(, int64()+)
			if  != nil {
				if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_END_OF_FILE &&  != 0 {
					return , nil
				}
				return 0, 
			}

			 += copy([:], )

			if  {
				return , nil
			}
		}
	}
}

func ( *File) ( int,  int64) ( []byte,  bool,  error) {
	, ,  := .fs.loanCredit()
	defer func() {
		if  != nil {
			.fs.chargeCredit()
		}
	}()
	if  != nil {
		return nil, false, 
	}

	 := &ReadRequest{
		Padding:         0,
		Flags:           0,
		Length:          uint32(),
		Offset:          uint64(),
		MinimumCount:    1, // for returning EOF
		Channel:         0,
		RemainingBytes:  0,
		ReadChannelInfo: nil,
	}

	.FileId = .fd

	.CreditCharge = 

	,  := .sendRecv(SMB2_READ, )
	if  != nil {
		return nil, false, 
	}

	 := ReadResponseDecoder()
	if .IsInvalid() {
		return nil, false, &InvalidResponseError{"broken read response format"}
	}

	 = .Data()

	return , len() < , nil
}

func ( *File) ( int) ( []os.FileInfo,  error) {
	.m.Lock()
	defer .m.Unlock()

	if !.noMoreFiles {
		if .dirents == nil {
			.dirents = []os.FileInfo{}
		}
		for  <= 0 ||  > len(.dirents) {
			,  := .readdir("*")
			if len() > 0 {
				.dirents = append(.dirents, ...)
			}
			if  != nil {
				if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_NO_MORE_FILES {
					.noMoreFiles = true
					break
				}
				return nil, &os.PathError{Op: "readdir", Path: .name, Err: }
			}
		}
	}

	 = .dirents

	if  > 0 {
		if len() == 0 {
			return , io.EOF
		}

		if len() <  {
			.dirents = []os.FileInfo{}
			return , nil
		}

		.dirents = [:]
		return [:], nil

	}

	.dirents = []os.FileInfo{}

	return , nil
}

func ( *File) ( int) ( []string,  error) {
	,  := .Readdir()
	if  != nil {
		return nil, 
	}

	 = make([]string, len())

	for ,  := range  {
		[] = .Name()
	}

	return , nil
}

// Seek implements io.Seeker.
func ( *File) ( int64,  int) ( int64,  error) {
	.m.Lock()
	defer .m.Unlock()

	,  = .seek(, )
	if  != nil {
		return , &os.PathError{Op: "seek", Path: .name, Err: }
	}
	return , nil
}

func ( *File) ( int64,  int) ( int64,  error) {
	switch  {
	case io.SeekStart:
		.offset = 
	case io.SeekCurrent:
		.offset += 
	case io.SeekEnd:
		 := &QueryInfoRequest{
			InfoType:              SMB2_0_INFO_FILE,
			FileInfoClass:         FileStandardInformation,
			AdditionalInformation: 0,
			Flags:                 0,
			OutputBufferLength:    24,
		}

		,  := .queryInfo()
		if  != nil {
			return -1, 
		}

		 := FileStandardInformationDecoder()
		if .IsInvalid() {
			return -1, &InvalidResponseError{"broken query info response format"}
		}

		.offset =  + .EndOfFile()
	default:
		return -1, os.ErrInvalid
	}

	return .offset, nil
}

func ( *File) () (os.FileInfo, error) {
	,  := .stat()
	if  != nil {
		return nil, &os.PathError{Op: "stat", Path: .name, Err: }
	}
	return , nil
}

func ( *File) () (os.FileInfo, error) {
	 := &QueryInfoRequest{
		InfoType:              SMB2_0_INFO_FILE,
		FileInfoClass:         FileAllInformation,
		AdditionalInformation: 0,
		Flags:                 0,
		OutputBufferLength:    uint32(.maxTransactSize()),
	}

	,  := .queryInfo()
	if  != nil {
		return nil, 
	}

	 := FileAllInformationDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken query info response format"}
	}

	 := .BasicInformation()
	 := .StandardInformation()

	return &FileStat{
		CreationTime:   time.Unix(0, .CreationTime().Nanoseconds()),
		LastAccessTime: time.Unix(0, .LastAccessTime().Nanoseconds()),
		LastWriteTime:  time.Unix(0, .LastWriteTime().Nanoseconds()),
		ChangeTime:     time.Unix(0, .ChangeTime().Nanoseconds()),
		EndOfFile:      .EndOfFile(),
		AllocationSize: .AllocationSize(),
		FileAttributes: .FileAttributes(),
		FileName:       base(.name),
	}, nil
}

func ( *File) () (FileFsInfo, error) {
	,  := .statfs()
	if  != nil {
		return nil, &os.PathError{Op: "statfs", Path: .name, Err: }
	}
	return , nil
}

type FileFsInfo interface {
	BlockSize() uint64
	FragmentSize() uint64
	TotalBlockCount() uint64
	FreeBlockCount() uint64
	AvailableBlockCount() uint64
}

type fileFsFullSizeInformation struct {
	TotalAllocationUnits           int64
	CallerAvailableAllocationUnits int64
	ActualAvailableAllocationUnits int64
	SectorsPerAllocationUnit       uint32
	BytesPerSector                 uint32
}

func ( *fileFsFullSizeInformation) () uint64 {
	return uint64(.BytesPerSector)
}

func ( *fileFsFullSizeInformation) () uint64 {
	return uint64(.SectorsPerAllocationUnit)
}

func ( *fileFsFullSizeInformation) () uint64 {
	return uint64(.TotalAllocationUnits)
}

func ( *fileFsFullSizeInformation) () uint64 {
	return uint64(.ActualAvailableAllocationUnits)
}

func ( *fileFsFullSizeInformation) () uint64 {
	return uint64(.CallerAvailableAllocationUnits)
}

func ( *File) () (FileFsInfo, error) {
	 := &QueryInfoRequest{
		InfoType:              SMB2_0_INFO_FILESYSTEM,
		FileInfoClass:         FileFsFullSizeInformation,
		AdditionalInformation: 0,
		Flags:                 0,
		OutputBufferLength:    32,
	}

	,  := .queryInfo()
	if  != nil {
		return nil, 
	}

	 := FileFsFullSizeInformationDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken query info response format"}
	}

	return &fileFsFullSizeInformation{
		TotalAllocationUnits:           .TotalAllocationUnits(),
		CallerAvailableAllocationUnits: .CallerAvailableAllocationUnits(),
		ActualAvailableAllocationUnits: .ActualAvailableAllocationUnits(),
		SectorsPerAllocationUnit:       .SectorsPerAllocationUnit(),
		BytesPerSector:                 .BytesPerSector(),
	}, nil
}

func ( *File) () ( error) {
	 := new(FlushRequest)
	.FileId = .fd

	.CreditCharge, _,  = .fs.loanCredit(0)
	defer func() {
		if  != nil {
			.fs.chargeCredit(.CreditCharge)
		}
	}()
	if  != nil {
		return &os.PathError{Op: "sync", Path: .name, Err: }
	}

	,  := .sendRecv(SMB2_FLUSH, )
	if  != nil {
		return &os.PathError{Op: "sync", Path: .name, Err: }
	}

	 := FlushResponseDecoder()
	if .IsInvalid() {
		return &os.PathError{Op: "sync", Path: .name, Err: &InvalidResponseError{"broken flush response format"}}
	}

	return nil
}

func ( *File) ( int64) error {
	if  < 0 {
		return os.ErrInvalid
	}

	 := .truncate()
	if  != nil {
		return &os.PathError{Op: "truncate", Path: .name, Err: }
	}
	return nil
}

func ( *File) ( int64) error {
	 := &SetInfoRequest{
		FileInfoClass:         FileEndOfFileInformation,
		AdditionalInformation: 0,
		Input: &FileEndOfFileInformationEncoder{
			EndOfFile: ,
		},
	}

	 := .setInfo()
	if  != nil {
		return 
	}
	return nil
}

func ( *File) ( os.FileMode) error {
	 := .chmod()
	if  != nil {
		return &os.PathError{Op: "chmod", Path: .name, Err: }
	}
	return nil
}

func ( *File) ( os.FileMode) error {
	 := &QueryInfoRequest{
		InfoType:              SMB2_0_INFO_FILE,
		FileInfoClass:         FileBasicInformation,
		AdditionalInformation: 0,
		Flags:                 0,
		OutputBufferLength:    40,
	}

	,  := .queryInfo()
	if  != nil {
		return 
	}

	 := FileBasicInformationDecoder()
	if .IsInvalid() {
		return &InvalidResponseError{"broken query info response format"}
	}

	 := .FileAttributes()

	if &0200 != 0 {
		 &^= FILE_ATTRIBUTE_READONLY
	} else {
		 |= FILE_ATTRIBUTE_READONLY
	}

	 := &SetInfoRequest{
		FileInfoClass:         FileBasicInformation,
		AdditionalInformation: 0,
		Input: &FileBasicInformationEncoder{
			FileAttributes: ,
		},
	}

	 = .setInfo()
	if  != nil {
		return 
	}
	return nil
}

func ( *File) ( []byte) ( int,  error) {
	.m.Lock()
	defer .m.Unlock()

	,  := .seek(0, io.SeekCurrent)
	if  != nil {
		return -1, &os.PathError{Op: "write", Path: .name, Err: }
	}

	,  = .writeAt(, )
	if  != 0 {
		if ,  := .seek(+int64(), io.SeekStart);  == nil {
			 = 
		}
	}
	if  != nil {
		return , &os.PathError{Op: "write", Path: .name, Err: }
	}

	return , nil
}

// WriteAt implements io.WriterAt.
func ( *File) ( []byte,  int64) ( int,  error) {
	,  = .writeAt(, )
	if  != nil {
		return , &os.PathError{Op: "write", Path: .name, Err: }
	}
	return , nil
}

func ( *File) ( []byte,  int64) ( int,  error) {
	if  < 0 {
		return -1, os.ErrInvalid
	}

	if len() == 0 {
		return 0, nil
	}

	 := .maxWriteSize()

	for {
		switch {
		case len()- == 0:
			return , nil
		case len()- <= :
			,  := .writeAtChunk([:], int64()+)
			if  != nil {
				return -1, 
			}

			 += 
		default:
			,  := .writeAtChunk([:+], int64()+)
			if  != nil {
				return -1, 
			}

			 += 
		}
	}
}

// writeAt allows partial write
func ( *File) ( []byte,  int64) ( int,  error) {
	, ,  := .fs.loanCredit(len())
	defer func() {
		if  != nil {
			.fs.chargeCredit()
		}
	}()
	if  != nil {
		return 0, 
	}

	 := &WriteRequest{
		Flags:            0,
		Channel:          0,
		RemainingBytes:   0,
		Offset:           uint64(),
		WriteChannelInfo: nil,
		Data:             [:],
	}

	.FileId = .fd

	.CreditCharge = 

	,  := .sendRecv(SMB2_WRITE, )
	if  != nil {
		return 0, 
	}

	 := WriteResponseDecoder()
	if .IsInvalid() {
		return 0, &InvalidResponseError{"broken write response format"}
	}

	return int(.Count()), nil
}

func copyBuffer( io.Reader,  io.Writer,  []byte) ( int64,  error) {
	for {
		,  := .Read()
		if  > 0 {
			,  := .Write([:])
			if  > 0 {
				 += int64()
			}
			if  != nil {
				 = 
				break
			}
			if  !=  {
				 = io.ErrShortWrite
				break
			}
		}
		if  != nil {
			if  != io.EOF {
				 = 
			}
			break
		}
	}
	return
}

func ( *File) ( *File) ( bool,  int64,  error) {
	.m.Lock()
	defer .m.Unlock()

	 := &IoctlRequest{
		CtlCode:           FSCTL_SRV_REQUEST_RESUME_KEY,
		OutputOffset:      0,
		OutputCount:       0,
		MaxInputResponse:  0,
		MaxOutputResponse: 32,
		Flags:             SMB2_0_IOCTL_IS_FSCTL,
	}

	,  := .ioctl()
	if  != nil {
		if ,  := .(*ResponseError);  && NtStatus(.Code) == STATUS_NOT_SUPPORTED {
			return false, -1, nil
		}

		return true, -1, &os.LinkError{Op: "copy", Old: .name, New: .name, Err: }

	}

	 := SrvRequestResumeKeyResponseDecoder()
	if .IsInvalid() {
		return true, -1, &os.LinkError{Op: "copy", Old: .name, New: .name, Err: &InvalidResponseError{"broken srv request resume key response format"}}
	}

	,  := .seek(0, io.SeekCurrent)
	if  != nil {
		return true, -1, &os.LinkError{Op: "copy", Old: .name, New: .name, Err: }
	}

	,  := .seek(0, io.SeekEnd)
	if  != nil {
		return true, -1, &os.LinkError{Op: "copy", Old: .name, New: .name, Err: }
	}

	,  := .seek(0, io.SeekCurrent)
	if  != nil {
		return true, -1, &os.LinkError{Op: "copy", Old: .name, New: .name, Err: }
	}

	var  []*SrvCopychunk

	 := 

	for {
		const  = 1024 * 1024
		const  = 16 * 1024 * 1024
		// https://msdn.microsoft.com/en-us/library/cc512134(v=vs.85).aspx

		if  <  {
			 :=  / 

			 = make([]*SrvCopychunk, , +1)
			for  := range  {
				[] = &SrvCopychunk{
					SourceOffset:  + int64()*,
					TargetOffset:  + int64()*,
					Length:       ,
				}
			}

			 %= 
			if  != 0 {
				 = append(, &SrvCopychunk{
					SourceOffset:  + int64()*,
					TargetOffset:  + int64()*,
					Length:       uint32(),
				})
				 = 0
			}
		} else {
			 = make([]*SrvCopychunk, 16)
			for  := range  {
				[] = &SrvCopychunk{
					SourceOffset:  + int64()*,
					TargetOffset:  + int64()*,
					Length:       ,
				}
			}

			 -= 
		}

		 := &SrvCopychunkCopy{
			Chunks: ,
		}

		copy(.SourceKey[:], .ResumeKey())

		 := &IoctlRequest{
			CtlCode:           FSCTL_SRV_COPYCHUNK,
			OutputOffset:      0,
			OutputCount:       0,
			MaxInputResponse:  0,
			MaxOutputResponse: 24,
			Flags:             SMB2_0_IOCTL_IS_FSCTL,
			Input:             ,
		}

		,  = .ioctl()
		if  != nil {
			return true, -1, &os.LinkError{Op: "copy", Old: .name, New: .name, Err: }
		}

		 := SrvCopychunkResponseDecoder()
		if .IsInvalid() {
			return true, -1, &os.LinkError{Op: "copy", Old: .name, New: .name, Err: &InvalidResponseError{"broken srv copy chunk response format"}}
		}

		 += int64(.TotalBytesWritten())

		if  == 0 {
			return true, , nil
		}
	}
}

// ReadFrom implements io.ReadFrom.
// If r is *File on the same *Share as f, it invokes server-side copy.
func ( *File) ( io.Reader) ( int64,  error) {
	,  := .(*File)
	if  && .fs == .fs {
		if , ,  := .copyTo();  {
			return , 
		}

		 := .maxReadSize()
		if  := .maxWriteSize();  <  {
			 = 
		}

		return copyBuffer(, , make([]byte, ))
	}

	return copyBuffer(, , make([]byte, .maxWriteSize()))
}

// WriteTo implements io.WriteTo.
// If w is *File on the same *Share as f, it invokes server-side copy.
func ( *File) ( io.Writer) ( int64,  error) {
	,  := .(*File)
	if  && .fs == .fs {
		if , ,  := .copyTo();  {
			return , 
		}

		 := .maxReadSize()
		if  := .maxWriteSize();  <  {
			 = 
		}

		return copyBuffer(, , make([]byte, ))
	}

	return copyBuffer(, , make([]byte, .maxReadSize()))
}

func ( *File) ( string) ( int,  error) {
	return .Write([]byte())
}

func ( *File) ( Encoder) int {
	if  == nil {
		return 0
	}
	return .Size()
}

func ( *File) ( *IoctlRequest) ( []byte,  error) {
	 := .encodeSize(.Input) + int(.OutputCount)
	if  < int(.MaxOutputResponse+.MaxInputResponse) {
		 = int(.MaxOutputResponse + .MaxInputResponse)
	}

	if .maxTransactSize() <  {
		return nil, &InternalError{fmt.Sprintf("payload size %d exceeds max transact size %d", , .maxTransactSize())}
	}

	.CreditCharge, _,  = .fs.loanCredit()
	defer func() {
		if  != nil {
			.fs.chargeCredit(.CreditCharge)
		}
	}()
	if  != nil {
		return nil, 
	}

	.FileId = .fd

	,  := .sendRecv(SMB2_IOCTL, )
	if  != nil {
		 := IoctlResponseDecoder()
		if .IsInvalid() {
			return nil, 
		}
		return .Output(), 
	}

	 := IoctlResponseDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken ioctl response format"}
	}

	return .Output(), nil
}

func ( *File) ( string) ( []os.FileInfo,  error) {
	 := &QueryDirectoryRequest{
		FileInfoClass:      FileDirectoryInformation,
		Flags:              0,
		FileIndex:          0,
		OutputBufferLength: uint32(.maxTransactSize()),
		FileName:           ,
	}

	 := int(.OutputBufferLength)

	if .maxTransactSize() <  {
		return nil, &InternalError{fmt.Sprintf("payload size %d exceeds max transact size %d", , .maxTransactSize())}
	}

	.CreditCharge, _,  = .fs.loanCredit()
	defer func() {
		if  != nil {
			.fs.chargeCredit(.CreditCharge)
		}
	}()
	if  != nil {
		return nil, 
	}

	.FileId = .fd

	,  := .sendRecv(SMB2_QUERY_DIRECTORY, )
	if  != nil {
		return nil, 
	}

	 := QueryDirectoryResponseDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken query directory response format"}
	}

	 := .OutputBuffer()

	for {
		 := FileDirectoryInformationDecoder()
		if .IsInvalid() {
			return nil, &InvalidResponseError{"broken query directory response format"}
		}

		 := .FileName()

		if  != "." &&  != ".." {
			 = append(, &FileStat{
				CreationTime:   time.Unix(0, .CreationTime().Nanoseconds()),
				LastAccessTime: time.Unix(0, .LastAccessTime().Nanoseconds()),
				LastWriteTime:  time.Unix(0, .LastWriteTime().Nanoseconds()),
				ChangeTime:     time.Unix(0, .ChangeTime().Nanoseconds()),
				EndOfFile:      .EndOfFile(),
				AllocationSize: .AllocationSize(),
				FileAttributes: .FileAttributes(),
				FileName:       ,
			})
		}

		 := .NextEntryOffset()
		if  == 0 {
			return , nil
		}

		 = [:]
	}
}

func ( *File) ( *QueryInfoRequest) ( []byte,  error) {
	 := .encodeSize(.Input)
	if  < int(.OutputBufferLength) {
		 = int(.OutputBufferLength)
	}

	if .maxTransactSize() <  {
		return nil, &InternalError{fmt.Sprintf("payload size %d exceeds max transact size %d", , .maxTransactSize())}
	}

	.CreditCharge, _,  = .fs.loanCredit()
	defer func() {
		if  != nil {
			.fs.chargeCredit(.CreditCharge)
		}
	}()
	if  != nil {
		return nil, 
	}

	.FileId = .fd

	,  := .sendRecv(SMB2_QUERY_INFO, )
	if  != nil {
		return nil, 
	}

	 := QueryInfoResponseDecoder()
	if .IsInvalid() {
		return nil, &InvalidResponseError{"broken query info response format"}
	}

	return .OutputBuffer(), nil
}

func ( *File) ( *SetInfoRequest) ( error) {
	 := .encodeSize(.Input)

	if .maxTransactSize() <  {
		return &InternalError{fmt.Sprintf("payload size %d exceeds max transact size %d", , .maxTransactSize())}
	}

	.CreditCharge, _,  = .fs.loanCredit()
	defer func() {
		if  != nil {
			.fs.chargeCredit(.CreditCharge)
		}
	}()
	if  != nil {
		return 
	}

	.FileId = .fd

	.InfoType = SMB2_0_INFO_FILE

	,  := .sendRecv(SMB2_SET_INFO, )
	if  != nil {
		return 
	}

	 := SetInfoResponseDecoder()
	if .IsInvalid() {
		return &InvalidResponseError{"broken set info response format"}
	}

	return nil
}

func ( *File) ( uint16,  Packet) ( []byte,  error) {
	return .fs.sendRecv(, )
}

type FileStat struct {
	CreationTime   time.Time
	LastAccessTime time.Time
	LastWriteTime  time.Time
	ChangeTime     time.Time
	EndOfFile      int64
	AllocationSize int64
	FileAttributes uint32
	FileName       string
}

func ( *FileStat) () string {
	return .FileName
}

func ( *FileStat) () int64 {
	return .EndOfFile
}

func ( *FileStat) () os.FileMode {
	var  os.FileMode

	if .FileAttributes&FILE_ATTRIBUTE_DIRECTORY != 0 {
		 |= os.ModeDir | 0111
	}

	if .FileAttributes&FILE_ATTRIBUTE_READONLY != 0 {
		 |= 0444
	} else {
		 |= 0666
	}

	if .FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT != 0 {
		 |= os.ModeSymlink
	}

	return 
}

func ( *FileStat) () time.Time {
	return .LastWriteTime
}

func ( *FileStat) () bool {
	return .Mode().IsDir()
}

func ( *FileStat) () interface{} {
	return 
}