1// Copyright 2016 Keybase Inc. All rights reserved.
2// Use of this source code is governed by a BSD
3// license that can be found in the LICENSE file.
4
5package dokan
6
7import (
8	"context"
9	"strconv"
10	"time"
11
12	"github.com/keybase/client/go/kbfs/dokan/winacl"
13)
14
15// Config is the configuration used for a mount.
16type Config struct {
17	// Path is the path to mount, e.g. `L:`. Must be set.
18	Path string
19	// FileSystem is the filesystem implementation. Must be set.
20	FileSystem FileSystem
21	// MountFlags for this filesystem instance. Is optional.
22	MountFlags MountFlag
23	// DllPath is the optional full path to dokan1.dll.
24	// Empty causes dokan1.dll to be loaded from the system directory.
25	// Only the first load of a dll determines the path -
26	// further instances in the same process will use
27	// the same instance regardless of path.
28	DllPath string
29}
30
31// FileSystem is the inteface for filesystems in Dokan.
32type FileSystem interface {
33	// WithContext returns a context for a new request. If the CancelFunc
34	// is not null, it is called after the request is done. The most minimal
35	// implementation is
36	// `func (*T)WithContext(c context.Context) { return c, nil }`.
37	WithContext(context.Context) (context.Context, context.CancelFunc)
38
39	// CreateFile is called to open and create files.
40	CreateFile(ctx context.Context, fi *FileInfo, data *CreateData) (file File, status CreateStatus, err error)
41
42	// GetDiskFreeSpace returns information about disk free space.
43	// Called quite often by Explorer.
44	GetDiskFreeSpace(ctx context.Context) (FreeSpace, error)
45
46	// GetVolumeInformation returns information about the volume.
47	GetVolumeInformation(ctx context.Context) (VolumeInformation, error)
48
49	// MoveFile corresponds to rename.
50	MoveFile(ctx context.Context, sourceHandle File, sourceFileInfo *FileInfo, targetPath string, replaceExisting bool) error
51
52	// ErrorPrint is called when dokan needs notify the program of an error message.
53	// A sensible approach is to print the error.
54	ErrorPrint(error)
55
56	// Printf is for information level messages.
57	Printf(format string, v ...interface{})
58}
59
60// CreateStatus marks status of successfull create/open operations.
61type CreateStatus uint32
62
63const (
64	// 1 Bit 0 == always set for valid values,
65	// 2 Bit 1 == directory or not
66	// 4 Bit 2 == new or not
67	isValid = 1
68	isDir   = 2
69	isNew   = 4
70
71	// NewDir for newly created directories.
72	NewDir = CreateStatus(isNew | isDir | isValid)
73	// NewFile for newly created files.
74	NewFile = CreateStatus(isNew | isValid)
75	// ExistingDir for newly created directories.
76	ExistingDir = CreateStatus(isDir | isValid)
77	// ExistingFile for newly created files.
78	ExistingFile = CreateStatus(isValid)
79)
80
81// IsDir tells whether a CreateStatus is about a directory or a file.
82func (cst CreateStatus) IsDir() bool {
83	return (cst & 2) != 0
84}
85
86// IsNew tells whether a CreateStatus is about a new or existing object.
87func (cst CreateStatus) IsNew() bool {
88	return (cst & 4) != 0
89}
90
91// MountFlag is the type for Dokan mount flags.
92type MountFlag uint32
93
94// Flags for mounting the filesystem. See Dokan documentation for these.
95const (
96	CDebug         = kbfsLibdokanDebug
97	CStderr        = kbfsLibdokanStderr
98	Removable      = kbfsLibdokanRemovable
99	MountManager   = kbfsLibdokanMountManager
100	CurrentSession = kbfsLibdokanCurrentSession
101	// UseFindFilesWithPattern enables FindFiles calls to be with a search
102	// pattern string. Otherwise the string will be empty in all calls.
103	UseFindFilesWithPattern = kbfsLibdokanUseFindFilesWithPattern
104)
105
106// CreateData contains all the info needed to create a file.
107type CreateData struct {
108	DesiredAccess     uint32
109	FileAttributes    FileAttribute
110	ShareAccess       uint32
111	CreateDisposition CreateDisposition
112	CreateOptions     uint32
113}
114
115// ReturningFileAllowed answers whether returning a file is allowed by
116// CreateOptions.
117func (cd *CreateData) ReturningFileAllowed() error {
118	if cd.CreateOptions&FileDirectoryFile != 0 {
119		return ErrNotADirectory
120	}
121	return nil
122}
123
124// ReturningDirAllowed answers whether returning a directory is allowed by
125// CreateOptions.
126func (cd *CreateData) ReturningDirAllowed() error {
127	if cd.CreateOptions&FileNonDirectoryFile != 0 {
128		return ErrFileIsADirectory
129	}
130	return nil
131}
132
133// CreateDisposition marks whether to create or open a file. Not a bitmask.
134type CreateDisposition uint32
135
136// File creation flags for CreateFile. This is not a bitmask.
137const (
138	FileSupersede   = CreateDisposition(0)
139	FileOpen        = CreateDisposition(1)
140	FileCreate      = CreateDisposition(2)
141	FileOpenIf      = CreateDisposition(3)
142	FileOverwrite   = CreateDisposition(4)
143	FileOverwriteIf = CreateDisposition(5)
144)
145
146// CreateOptions flags. These are bitmask flags.
147const (
148	FileDirectoryFile    = 0x1
149	FileNonDirectoryFile = 0x40
150	FileOpenReparsePoint = 0x00200000
151)
152
153// FileAttribute is the type of a directory entry in Stat.
154type FileAttribute uint32
155
156// File attribute bit masks - same as syscall but provided for all platforms.
157const (
158	FileAttributeReadonly     = FileAttribute(0x00000001)
159	FileAttributeHidden       = FileAttribute(0x00000002)
160	FileAttributeSystem       = FileAttribute(0x00000004)
161	FileAttributeDirectory    = FileAttribute(0x00000010)
162	FileAttributeArchive      = FileAttribute(0x00000020)
163	FileAttributeNormal       = FileAttribute(0x00000080)
164	FileAttributeReparsePoint = FileAttribute(0x00000400)
165	IOReparseTagSymlink       = 0xA000000C
166)
167
168// File is the interface for files and directories.
169type File interface {
170	// ReadFile implements read for dokan.
171	ReadFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
172	// WriteFile implements write for dokan.
173	WriteFile(ctx context.Context, fi *FileInfo, bs []byte, offset int64) (int, error)
174	// FlushFileBuffers corresponds to fsync.
175	FlushFileBuffers(ctx context.Context, fi *FileInfo) error
176
177	// GetFileInformation - corresponds to stat.
178	GetFileInformation(ctx context.Context, fi *FileInfo) (*Stat, error)
179
180	// FindFiles is the readdir. The function is a callback that should be called
181	// with each file. The same NamedStat may be reused for subsequent calls.
182	//
183	// Pattern will be an empty string unless UseFindFilesWithPattern is enabled - then
184	// it may be a pattern like `*.png` to match. All implementations must be prepared
185	// to handle empty strings as patterns.
186	FindFiles(ctx context.Context, fi *FileInfo, pattern string, fillStatCallback func(*NamedStat) error) error
187
188	// SetFileTime sets the file time. Test times with .IsZero
189	// whether they should be set.
190	SetFileTime(ctx context.Context, fi *FileInfo, creation time.Time, lastAccess time.Time, lastWrite time.Time) error
191	// SetFileAttributes is for setting file attributes.
192	SetFileAttributes(ctx context.Context, fi *FileInfo, fileAttributes FileAttribute) error
193
194	// SetEndOfFile truncates the file. May be used to extend a file with zeros.
195	SetEndOfFile(ctx context.Context, fi *FileInfo, length int64) error
196	// SetAllocationSize see FILE_ALLOCATION_INFORMATION on MSDN.
197	// For simple semantics if length > filesize then ignore else truncate(length).
198	SetAllocationSize(ctx context.Context, fi *FileInfo, length int64) error
199
200	LockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error
201	UnlockFile(ctx context.Context, fi *FileInfo, offset int64, length int64) error
202
203	GetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error
204	SetFileSecurity(ctx context.Context, fi *FileInfo, si winacl.SecurityInformation, sd *winacl.SecurityDescriptor) error
205
206	// CanDeleteFile and CanDeleteDirectory should check whether the file/directory
207	// can be deleted. The actual deletion should be done by checking
208	// FileInfo.IsDeleteOnClose in Cleanup.
209	CanDeleteFile(ctx context.Context, fi *FileInfo) error
210	CanDeleteDirectory(ctx context.Context, fi *FileInfo) error
211	// Cleanup is called after the last handle from userspace is closed.
212	// Cleanup must perform actual deletions marked from CanDelete*
213	// by checking FileInfo.IsDeleteOnClose if the filesystem supports
214	// deletions.
215	Cleanup(ctx context.Context, fi *FileInfo)
216	// CloseFile is called when closing a handle to the file.
217	CloseFile(ctx context.Context, fi *FileInfo)
218}
219
220// FreeSpace - semantics as with WINAPI GetDiskFreeSpaceEx
221type FreeSpace struct {
222	FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes uint64
223}
224
225// VolumeInformation - see WINAPI GetVolumeInformation for hints
226type VolumeInformation struct {
227	VolumeName             string
228	VolumeSerialNumber     uint32
229	MaximumComponentLength uint32
230	FileSystemFlags        FileSystemFlags
231	FileSystemName         string
232}
233
234// FileSystemFlags holds flags for filesystem features.
235type FileSystemFlags uint32
236
237// Various FileSystemFlags constants, see winapi documentation for details.
238const (
239	FileCasePreservedNames         = FileSystemFlags(0x2)
240	FileCaseSensitiveSearch        = FileSystemFlags(0x1)
241	FileFileCompression            = FileSystemFlags(0x10)
242	FileNamedStreams               = FileSystemFlags(0x40000)
243	FilePersistentAcls             = FileSystemFlags(0x8)
244	FileReadOnlyVolume             = FileSystemFlags(0x80000)
245	FileSequentalWriteOnce         = FileSystemFlags(0x100000)
246	FileSupportsEncryption         = FileSystemFlags(0x20000)
247	FileSupportsExtendedAttributes = FileSystemFlags(0x800000)
248	FileSupportsHardLinks          = FileSystemFlags(0x400000)
249	FileSupportObjectIDs           = FileSystemFlags(0x10000)
250	FileSupportsOpenByFileID       = FileSystemFlags(0x1000000)
251	FileSupportsRemoteStorage      = FileSystemFlags(0x100)
252	FileSupportsReparsePoints      = FileSystemFlags(0x80)
253	FileSupportsSparseFiles        = FileSystemFlags(0x40)
254	FileSupportsTransactions       = FileSystemFlags(0x200000)
255	FileSupportsUsnJournal         = FileSystemFlags(0x2000000)
256	FileUnicodeOnDisk              = FileSystemFlags(0x4)
257	FileVolumeIsCompressed         = FileSystemFlags(0x8000)
258	FileVolumeQuotas               = FileSystemFlags(0x20)
259)
260
261// Stat is for GetFileInformation and friends.
262type Stat struct {
263	// Timestamps for the file
264	Creation, LastAccess, LastWrite time.Time
265	// FileSize is the size of the file in bytes
266	FileSize int64
267	// FileIndex is a 64 bit (nearly) unique ID of the file
268	FileIndex uint64
269	// FileAttributes bitmask holds the file attributes.
270	FileAttributes FileAttribute
271	// VolumeSerialNumber is the serial number of the volume (0 is fine)
272	VolumeSerialNumber uint32
273	// NumberOfLinks can be omitted, if zero set to 1.
274	NumberOfLinks uint32
275	// ReparsePointTag is for WIN32_FIND_DATA dwReserved0 for reparse point tags, typically it can be omitted.
276	ReparsePointTag uint32
277}
278
279// NamedStat is used to for stat responses that require file names.
280// If the name is longer than a DOS-name, insert the corresponding
281// DOS-name to ShortName.
282type NamedStat struct {
283	Name      string
284	ShortName string
285	Stat
286}
287
288// NtStatus is a type implementing error interface that corresponds
289// to NTSTATUS. It can be used to set the exact error/status code
290// from the filesystem.
291type NtStatus uint32
292
293func (n NtStatus) Error() string {
294	return "NTSTATUS=" + strconv.FormatUint(uint64(n), 16)
295}
296
297const (
298	// ErrAccessDenied - access denied (EPERM)
299	ErrAccessDenied = NtStatus(0xC0000022)
300	// ErrObjectNameNotFound - filename does not exist (ENOENT)
301	ErrObjectNameNotFound = NtStatus(0xC0000034)
302	// ErrObjectNameCollision - a pathname already exists (EEXIST)
303	ErrObjectNameCollision = NtStatus(0xC0000035)
304	// ErrObjectPathNotFound - a pathname does not exist (ENOENT)
305	ErrObjectPathNotFound = NtStatus(0xC000003A)
306	// ErrNotSupported - not supported.
307	ErrNotSupported = NtStatus(0xC00000BB)
308	// ErrFileIsADirectory - file is a directory.
309	ErrFileIsADirectory = NtStatus(0xC00000BA)
310	// ErrDirectoryNotEmpty - wanted an empty dir - it is not empty.
311	ErrDirectoryNotEmpty = NtStatus(0xC0000101)
312	// ErrNotADirectory - wanted a directory - it is not a directory.
313	ErrNotADirectory = NtStatus(0xC0000103)
314	// ErrFileAlreadyExists - file already exists - fatal.
315	ErrFileAlreadyExists = NtStatus(0xC0000035)
316	// ErrNotSameDevice - MoveFile is denied, please use copy+delete.
317	ErrNotSameDevice = NtStatus(0xC00000D4)
318	// StatusBufferOverflow - buffer space too short for return value.
319	StatusBufferOverflow = NtStatus(0x80000005)
320	// StatusObjectNameExists - already exists, may be non-fatal...
321	StatusObjectNameExists = NtStatus(0x40000000)
322)
323