mirror of
https://github.com/dominicusin/zfs-win
synced 2025-08-30 13:57:58 +00:00
dokan callbacks, directory listing, file attributes
This commit is contained in:
parent
8f21175190
commit
e49cdd1a4a
326
dokan/dokan.h
Normal file
326
dokan/dokan.h
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
Dokan : user-mode file system library for Windows
|
||||
|
||||
Copyright (C) 2008 Hiroki Asakawa info@dokan-dev.net
|
||||
|
||||
http://dokan-dev.net/en
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 of the License, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _DOKAN_H_
|
||||
#define _DOKAN_H_
|
||||
|
||||
#define DOKAN_DRIVER_NAME L"dokan.sys"
|
||||
|
||||
#ifndef _M_X64
|
||||
#ifdef _EXPORTING
|
||||
#define DOKANAPI __declspec(dllimport) __stdcall
|
||||
#else
|
||||
#define DOKANAPI __declspec(dllexport) __stdcall
|
||||
#endif
|
||||
#else
|
||||
#define DOKANAPI
|
||||
#endif
|
||||
|
||||
#define DOKAN_CALLBACK __stdcall
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#define DOKAN_OPTION_DEBUG 1 // ouput debug message
|
||||
#define DOKAN_OPTION_STDERR 2 // ouput debug message to stderr
|
||||
#define DOKAN_OPTION_ALT_STREAM 4 // use alternate stream
|
||||
#define DOKAN_OPTION_KEEP_ALIVE 8 // use auto unmount
|
||||
#define DOKAN_OPTION_NETWORK 16 // use network drive
|
||||
#define DOKAN_OPTION_REMOVABLE 32 // use removable drive
|
||||
|
||||
typedef struct _DOKAN_OPTIONS {
|
||||
WCHAR DriveLetter; // drive letter to be mounted
|
||||
USHORT ThreadCount; // number of threads to be used
|
||||
ULONG Options; // combination of DOKAN_OPTIONS_*
|
||||
ULONG64 GlobalContext; // FileSystem can use this variable
|
||||
} DOKAN_OPTIONS, *PDOKAN_OPTIONS;
|
||||
|
||||
typedef struct _DOKAN_FILE_INFO {
|
||||
ULONG64 Context; // FileSystem can use this variable
|
||||
ULONG64 DokanContext; // Don't touch this
|
||||
PDOKAN_OPTIONS DokanOptions; // A pointer to DOKAN_OPTIONS which was passed to DokanMain.
|
||||
ULONG ProcessId; // process id for the thread that originally requested a given I/O operation
|
||||
UCHAR IsDirectory; // requesting a directory file
|
||||
UCHAR DeleteOnClose; // Delete on when "cleanup" is called
|
||||
UCHAR PagingIo; // Read or write is paging IO.
|
||||
UCHAR SynchronousIo; // Read or write is synchronous IO.
|
||||
UCHAR Nocache;
|
||||
UCHAR WriteToEndOfFile; // If true, write to the current end of file instead of Offset parameter.
|
||||
|
||||
} DOKAN_FILE_INFO, *PDOKAN_FILE_INFO;
|
||||
|
||||
|
||||
// FillFileData
|
||||
// add an entry in FindFiles
|
||||
// return 1 if buffer is full, otherwise 0
|
||||
// (currently never return 1)
|
||||
typedef int (WINAPI *PFillFindData) (PWIN32_FIND_DATAW, PDOKAN_FILE_INFO);
|
||||
|
||||
typedef struct _DOKAN_OPERATIONS {
|
||||
|
||||
// When an error occurs, return negative value.
|
||||
// Usually you should return GetLastError() * -1.
|
||||
|
||||
|
||||
// CreateFile
|
||||
// If file is a directory, CreateFile (not OpenDirectory) may be called.
|
||||
// In this case, CreateFile should return 0 when that directory can be opened.
|
||||
// You should set TRUE on DokanFileInfo->IsDirectory when file is a directory.
|
||||
// When CreationDisposition is CREATE_ALWAYS or OPEN_ALWAYS and a file already exists,
|
||||
// you should return ERROR_ALREADY_EXISTS(183) (not negative value)
|
||||
int (DOKAN_CALLBACK *CreateFile) (
|
||||
LPCWSTR, // FileName
|
||||
DWORD, // DesiredAccess
|
||||
DWORD, // ShareMode
|
||||
DWORD, // CreationDisposition
|
||||
DWORD, // FlagsAndAttributes
|
||||
//HANDLE, // TemplateFile
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
int (DOKAN_CALLBACK *OpenDirectory) (
|
||||
LPCWSTR, // FileName
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
int (DOKAN_CALLBACK *CreateDirectory) (
|
||||
LPCWSTR, // FileName
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
// When FileInfo->DeleteOnClose is true, you must delete the file in Cleanup.
|
||||
int (DOKAN_CALLBACK *Cleanup) (
|
||||
LPCWSTR, // FileName
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
int (DOKAN_CALLBACK *CloseFile) (
|
||||
LPCWSTR, // FileName
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
int (DOKAN_CALLBACK *ReadFile) (
|
||||
LPCWSTR, // FileName
|
||||
LPVOID, // Buffer
|
||||
DWORD, // NumberOfBytesToRead
|
||||
LPDWORD, // NumberOfBytesRead
|
||||
LONGLONG, // Offset
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *WriteFile) (
|
||||
LPCWSTR, // FileName
|
||||
LPCVOID, // Buffer
|
||||
DWORD, // NumberOfBytesToWrite
|
||||
LPDWORD, // NumberOfBytesWritten
|
||||
LONGLONG, // Offset
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *FlushFileBuffers) (
|
||||
LPCWSTR, // FileName
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *GetFileInformation) (
|
||||
LPCWSTR, // FileName
|
||||
LPBY_HANDLE_FILE_INFORMATION, // Buffer
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *FindFiles) (
|
||||
LPCWSTR, // PathName
|
||||
PFillFindData, // call this function with PWIN32_FIND_DATAW
|
||||
PDOKAN_FILE_INFO); // (see PFillFindData definition)
|
||||
|
||||
|
||||
// You should implement either FindFiles or FindFilesWithPattern
|
||||
int (DOKAN_CALLBACK *FindFilesWithPattern) (
|
||||
LPCWSTR, // PathName
|
||||
LPCWSTR, // SearchPattern
|
||||
PFillFindData, // call this function with PWIN32_FIND_DATAW
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *SetFileAttributes) (
|
||||
LPCWSTR, // FileName
|
||||
DWORD, // FileAttributes
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *SetFileTime) (
|
||||
LPCWSTR, // FileName
|
||||
CONST FILETIME*, // CreationTime
|
||||
CONST FILETIME*, // LastAccessTime
|
||||
CONST FILETIME*, // LastWriteTime
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
// You should not delete file on DeleteFile or DeleteDirectory.
|
||||
// When DeleteFile or DeleteDirectory, you must check whether
|
||||
// you can delete or not, and return 0 (when you can delete it)
|
||||
// or appropriate error codes such as -ERROR_DIR_NOT_EMPTY,
|
||||
// -ERROR_SHARING_VIOLATION.
|
||||
// When you return 0 (ERROR_SUCCESS), you get Cleanup with
|
||||
// FileInfo->DeleteOnClose set TRUE, you delete the file.
|
||||
int (DOKAN_CALLBACK *DeleteFile) (
|
||||
LPCWSTR, // FileName
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
int (DOKAN_CALLBACK *DeleteDirectory) (
|
||||
LPCWSTR, // FileName
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *MoveFile) (
|
||||
LPCWSTR, // ExistingFileName
|
||||
LPCWSTR, // NewFileName
|
||||
BOOL, // ReplaceExisiting
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *SetEndOfFile) (
|
||||
LPCWSTR, // FileName
|
||||
LONGLONG, // Length
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *SetAllocationSize) (
|
||||
LPCWSTR, // FileName
|
||||
LONGLONG, // Length
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *LockFile) (
|
||||
LPCWSTR, // FileName
|
||||
LONGLONG, // ByteOffset
|
||||
LONGLONG, // Length
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *UnlockFile) (
|
||||
LPCWSTR, // FileName
|
||||
LONGLONG,// ByteOffset
|
||||
LONGLONG,// Length
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
// Neither GetDiskFreeSpace nor GetVolumeInformation
|
||||
// save the DokanFileContext->Context.
|
||||
// Before these methods are called, CreateFile may not be called.
|
||||
// (ditto CloseFile and Cleanup)
|
||||
|
||||
// see Win32 API GetDiskFreeSpaceEx
|
||||
int (DOKAN_CALLBACK *GetDiskFreeSpace) (
|
||||
PULONGLONG, // FreeBytesAvailable
|
||||
PULONGLONG, // TotalNumberOfBytes
|
||||
PULONGLONG, // TotalNumberOfFreeBytes
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
// see Win32 API GetVolumeInformation
|
||||
int (DOKAN_CALLBACK *GetVolumeInformation) (
|
||||
LPWSTR, // VolumeNameBuffer
|
||||
DWORD, // VolumeNameSize in num of chars
|
||||
LPDWORD,// VolumeSerialNumber
|
||||
LPDWORD,// MaximumComponentLength in num of chars
|
||||
LPDWORD,// FileSystemFlags
|
||||
LPWSTR, // FileSystemNameBuffer
|
||||
DWORD, // FileSystemNameSize in num of chars
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
|
||||
int (DOKAN_CALLBACK *Unmount) (
|
||||
PDOKAN_FILE_INFO);
|
||||
|
||||
} DOKAN_OPERATIONS, *PDOKAN_OPERATIONS;
|
||||
|
||||
|
||||
|
||||
/* DokanMain returns error codes */
|
||||
#define DOKAN_SUCCESS 0
|
||||
#define DOKAN_ERROR -1 /* General Error */
|
||||
#define DOKAN_DRIVE_LETTER_ERROR -2 /* Bad Drive letter */
|
||||
#define DOKAN_DRIVER_INSTALL_ERROR -3 /* Can't install driver */
|
||||
#define DOKAN_START_ERROR -4 /* Driver something wrong */
|
||||
#define DOKAN_MOUNT_ERROR -5 /* Can't assign a drive letter */
|
||||
|
||||
|
||||
int DOKANAPI
|
||||
DokanMain(
|
||||
PDOKAN_OPTIONS DokanOptions,
|
||||
PDOKAN_OPERATIONS DokanOperations);
|
||||
|
||||
|
||||
BOOL DOKANAPI
|
||||
DokanUnmount(
|
||||
WCHAR DriveLetter);
|
||||
|
||||
|
||||
// DokanIsNameInExpression
|
||||
// check whether Name can match Expression
|
||||
// Expression can contain wildcard characters (? and *)
|
||||
BOOL DOKANAPI
|
||||
DokanIsNameInExpression(
|
||||
LPCWSTR Expression, // matching pattern
|
||||
LPCWSTR Name, // file name
|
||||
BOOL IgnoreCase);
|
||||
|
||||
|
||||
ULONG DOKANAPI
|
||||
DokanVersion();
|
||||
|
||||
ULONG DOKANAPI
|
||||
DokanDriverVersion();
|
||||
|
||||
// DokanResetTimeout
|
||||
// extends the time out of the current IO operation in driver.
|
||||
BOOL DOKANAPI
|
||||
DokanResetTimeout(
|
||||
ULONG Timeout, // timeout in millisecond
|
||||
PDOKAN_FILE_INFO DokanFileInfo);
|
||||
|
||||
|
||||
|
||||
// for internal use
|
||||
// don't call
|
||||
BOOL DOKANAPI
|
||||
DokanServiceInstall(
|
||||
LPCWSTR ServiceName,
|
||||
DWORD ServiceType,
|
||||
LPCWSTR ServiceFullPath);
|
||||
|
||||
BOOL DOKANAPI
|
||||
DokanServiceDelete(
|
||||
LPCWSTR ServiceName);
|
||||
|
||||
BOOL DOKANAPI
|
||||
DokanNetworkProviderInstall();
|
||||
|
||||
BOOL DOKANAPI
|
||||
DokanNetworkProviderUninstall();
|
||||
|
||||
BOOL DOKANAPI
|
||||
DokanSetDebugMode(ULONG Mode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _DOKAN_H_
|
BIN
dokan/dokan.lib
Normal file
BIN
dokan/dokan.lib
Normal file
Binary file not shown.
@ -50,6 +50,10 @@ namespace ZFS
|
||||
{
|
||||
m_bpl.push_back(new blkptr_t(bp[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(bp[i].lsize == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -62,6 +66,10 @@ namespace ZFS
|
||||
{
|
||||
l.push_back(new blkptr_t(bp[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(bp[i].lsize == 0);
|
||||
}
|
||||
}
|
||||
|
||||
m_bpl.insert(m_bpl.begin(), l.begin(), l.end());
|
||||
|
@ -21,25 +21,26 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "DataSet.h"
|
||||
#include "String.h"
|
||||
|
||||
namespace ZFS
|
||||
{
|
||||
DataSet::DataSet(Pool* pool)
|
||||
: m_pool(pool)
|
||||
, m_head(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
DataSet::~DataSet()
|
||||
{
|
||||
for(auto i = m_children.begin(); i != m_children.end(); i++)
|
||||
{
|
||||
delete *i;
|
||||
}
|
||||
RemoveAll();
|
||||
}
|
||||
|
||||
bool DataSet::Init(ObjectSet& os, const char* name, size_t root_index)
|
||||
{
|
||||
m_name = name != NULL ? name : m_pool->m_name.c_str();
|
||||
RemoveAll();
|
||||
|
||||
m_name = name;
|
||||
|
||||
dnode_phys_t dn;
|
||||
|
||||
@ -67,6 +68,16 @@ namespace ZFS
|
||||
|
||||
m_dataset = *(dsl_dataset_phys_t*)dn.bonus();
|
||||
|
||||
if(m_dataset.bp.type == DMU_OT_OBJSET)
|
||||
{
|
||||
m_head = new ObjectSet(m_pool);
|
||||
|
||||
if(!m_head->Init(&m_dataset.bp, 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(os.Read((size_t)m_dir.props_zapobj, &dn, DMU_OT_DSL_PROPS))
|
||||
{
|
||||
ZFS::ZapObject zap(m_pool);
|
||||
@ -107,6 +118,50 @@ namespace ZFS
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataSet::RemoveAll()
|
||||
{
|
||||
for(auto i = m_children.begin(); i != m_children.end(); i++)
|
||||
{
|
||||
delete *i;
|
||||
}
|
||||
|
||||
if(m_head != NULL)
|
||||
{
|
||||
delete m_head;
|
||||
|
||||
m_head = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void DataSet::SetDefaults(DataSet* parent)
|
||||
{
|
||||
// TODO
|
||||
|
||||
for(auto i = m_children.begin(); i != m_children.end(); i++)
|
||||
{
|
||||
(*i)->SetDefaults(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool DataSet::Init(blkptr_t* bp, size_t count)
|
||||
{
|
||||
ObjectSet os(m_pool);
|
||||
|
||||
if(!os.Init(bp, count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Init(os, m_pool->m_name.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SetDefaults(NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataSet::GetMountPoints(std::list<DataSet*>& mpl)
|
||||
{
|
||||
if(!m_mountpoint.empty())
|
||||
@ -119,4 +174,81 @@ namespace ZFS
|
||||
(*i)->GetMountPoints(mpl);
|
||||
}
|
||||
}
|
||||
|
||||
bool DataSet::Find(const wchar_t* path, dnode_phys_t& dn)
|
||||
{
|
||||
if(m_head == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring s = path;
|
||||
|
||||
for(size_t i = 0; i < s.size(); i++)
|
||||
{
|
||||
if(s[i] == '\\') s[i] = '/';
|
||||
}
|
||||
|
||||
if(s[0] != '/')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!m_head->Read("ROOT", &dn, DMU_OT_DIRECTORY_CONTENTS))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(s == L"/")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring::size_type i = 1;
|
||||
|
||||
do
|
||||
{
|
||||
if(dn.type != DMU_OT_DIRECTORY_CONTENTS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring::size_type j = s.find('/', i);
|
||||
|
||||
std::wstring dir = s.substr(i, j - i);
|
||||
|
||||
wprintf(L"%d-%d %s\n", i, j, dir.c_str());
|
||||
|
||||
i = j != std::string::npos ? j + 1 : std::string::npos;
|
||||
|
||||
ZFS::ZapObject zap(m_pool);
|
||||
|
||||
if(!zap.Init(dn.blkptr, dn.nblkptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name = Util::UTF16To8(dir.c_str());
|
||||
|
||||
uint64_t index = 0;
|
||||
|
||||
if(!zap.Lookup(name.c_str(), index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!m_head->Read((size_t)ZFS_DIRENT_OBJ(index), &dn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(dn.type != DMU_OT_DIRECTORY_CONTENTS && dn.type != DMU_OT_PLAIN_FILE_CONTENTS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while(i != std::string::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -30,21 +30,24 @@ namespace ZFS
|
||||
{
|
||||
Pool* m_pool;
|
||||
|
||||
bool Init(ObjectSet& os, const char* name = NULL, size_t root_index = -1);
|
||||
void RemoveAll();
|
||||
void SetDefaults(DataSet* parent);
|
||||
|
||||
public:
|
||||
dsl_dir_phys_t m_dir; // TODO: store its properties instead
|
||||
dsl_dataset_phys_t m_dataset; // TODO: store its properties instead
|
||||
std::string m_name;
|
||||
std::string m_mountpoint;
|
||||
std::list<DataSet*> m_children;
|
||||
ObjectSet* m_head;
|
||||
|
||||
public:
|
||||
DataSet(Pool* pool);
|
||||
virtual ~DataSet();
|
||||
|
||||
bool Init(ObjectSet& os, const char* name = NULL, size_t root_index = -1);
|
||||
|
||||
bool Init(blkptr_t* bp, size_t count);
|
||||
void GetMountPoints(std::list<DataSet*>& mpl);
|
||||
|
||||
// TODO: directory browsing functions, handle mount-points transparently
|
||||
bool Find(const wchar_t* path, dnode_phys_t& dn);
|
||||
};
|
||||
}
|
@ -194,7 +194,7 @@ namespace ZFS
|
||||
{
|
||||
Close();
|
||||
|
||||
m_handle = CreateFile(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, (HANDLE)NULL);
|
||||
m_handle = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, (HANDLE)NULL);
|
||||
|
||||
if(m_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@ -323,6 +323,18 @@ namespace ZFS
|
||||
|
||||
size_t Device::Read(void* buff, uint64_t size)
|
||||
{
|
||||
if(0)
|
||||
{
|
||||
LARGE_INTEGER li, li2;
|
||||
|
||||
li.QuadPart = 0;
|
||||
|
||||
if(SetFilePointerEx(m_handle, li, &li2, FILE_CURRENT))
|
||||
{
|
||||
printf("%I64d - %I64d (%I64d)\n", li2.QuadPart, li2.QuadPart + size, size);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD read = 0;
|
||||
|
||||
ReadFile(m_handle, buff, (DWORD)size, &read, NULL);
|
||||
|
@ -95,7 +95,7 @@ namespace ZFS
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!m_dnode_reader->Read(dn, sizeof(dnode_phys_t), sizeof(dnode_phys_t) * index))
|
||||
if(!m_dnode_reader->Read(dn, sizeof(dnode_phys_t), (uint64_t)index * sizeof(dnode_phys_t)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -35,11 +35,14 @@ namespace ZFS
|
||||
Close();
|
||||
}
|
||||
|
||||
bool Pool::Open(const char* name, const std::list<std::wstring>& paths)
|
||||
bool Pool::Open(const std::list<std::wstring>& paths, const char* name)
|
||||
{
|
||||
Close();
|
||||
|
||||
m_name = name;
|
||||
if(name != NULL)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
for(auto i = paths.begin(); i != paths.end(); i++)
|
||||
{
|
||||
@ -50,6 +53,11 @@ namespace ZFS
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_name.empty())
|
||||
{
|
||||
m_name = dev->m_desc.pool.name;
|
||||
}
|
||||
|
||||
if(m_name == dev->m_desc.pool.name && (m_guid == 0 || m_guid == dev->m_desc.pool.guid))
|
||||
{
|
||||
m_guid = dev->m_desc.pool.guid;
|
||||
|
@ -38,7 +38,7 @@ namespace ZFS
|
||||
Pool();
|
||||
virtual ~Pool();
|
||||
|
||||
bool Open(const char* name, const std::list<std::wstring>& paths);
|
||||
bool Open(const std::list<std::wstring>& paths, const char* name = NULL);
|
||||
void Close();
|
||||
|
||||
bool Read(std::vector<uint8_t>& buff, blkptr_t* bp, size_t count);
|
||||
|
249
zfs-win/String.cpp
Normal file
249
zfs-win/String.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
#include "stdafx.h"
|
||||
#include "String.h"
|
||||
|
||||
namespace Util
|
||||
{
|
||||
std::wstring Format(const wchar_t* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
wchar_t* buff = NULL;
|
||||
|
||||
std::wstring str;
|
||||
|
||||
int len = _vscwprintf(fmt, args) + 1;
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
buff = new wchar_t[len];
|
||||
|
||||
vswprintf_s(buff, len, fmt, args);
|
||||
|
||||
str = std::wstring(buff, len - 1);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
delete [] buff;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string Format(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
char* buff = NULL;
|
||||
|
||||
std::string str;
|
||||
|
||||
int len = _vscprintf(fmt, args) + 1;
|
||||
|
||||
if(len > 0)
|
||||
{
|
||||
buff = new char[len];
|
||||
|
||||
vsprintf_s(buff, len, fmt, args);
|
||||
|
||||
str = std::string(buff, len - 1);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
delete [] buff;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::wstring TrimLeft(const std::wstring& s, LPCWSTR chrs)
|
||||
{
|
||||
std::wstring::size_type i = s.find_first_not_of(chrs);
|
||||
|
||||
return i != std::wstring::npos ? s.substr(i) : std::wstring();
|
||||
}
|
||||
|
||||
std::wstring TrimRight(const std::wstring& s, LPCWSTR chrs)
|
||||
{
|
||||
std::wstring::size_type i = s.find_last_not_of(chrs);
|
||||
|
||||
return i != std::wstring::npos ? s.substr(0, i + 1) : s;
|
||||
}
|
||||
|
||||
std::wstring Trim(const std::wstring& s, LPCWSTR chrs)
|
||||
{
|
||||
return TrimLeft(TrimRight(s, chrs), chrs);
|
||||
}
|
||||
|
||||
std::string TrimLeft(const std::string& s, LPCSTR chrs)
|
||||
{
|
||||
std::string::size_type i = s.find_first_not_of(chrs);
|
||||
|
||||
return i != std::string::npos ? s.substr(i) : std::string();
|
||||
}
|
||||
|
||||
std::string TrimRight(const std::string& s, LPCSTR chrs)
|
||||
{
|
||||
std::string::size_type i = s.find_last_not_of(chrs);
|
||||
|
||||
return i != std::string::npos ? s.substr(0, i + 1) : s;
|
||||
}
|
||||
|
||||
std::string Trim(const std::string& s, LPCSTR chrs)
|
||||
{
|
||||
return TrimLeft(TrimRight(s, chrs), chrs);
|
||||
}
|
||||
|
||||
std::wstring MakeUpper(const std::wstring& s)
|
||||
{
|
||||
std::wstring str = s;
|
||||
|
||||
if(!str.empty()) _wcsupr(&str[0]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::wstring MakeLower(const std::wstring& s)
|
||||
{
|
||||
std::wstring str = s;
|
||||
|
||||
if(!str.empty()) _wcslwr(&str[0]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string MakeUpper(const std::string& s)
|
||||
{
|
||||
std::string str = s;
|
||||
|
||||
if(!str.empty()) strupr(&str[0]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string MakeLower(const std::string& s)
|
||||
{
|
||||
std::string str = s;
|
||||
|
||||
if(!str.empty()) strlwr(&str[0]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::wstring UTF8To16(LPCSTR s)
|
||||
{
|
||||
std::wstring ret;
|
||||
|
||||
int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0) - 1;
|
||||
|
||||
if(n >= 0)
|
||||
{
|
||||
wchar_t* buff = new wchar_t[n + 1];
|
||||
|
||||
n = MultiByteToWideChar(CP_UTF8, 0, s, -1, buff, n + 1);
|
||||
|
||||
if(n > 0)
|
||||
{
|
||||
ret = std::wstring(buff);
|
||||
}
|
||||
|
||||
delete [] buff;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string UTF16To8(LPCWSTR s)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
int n = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL) - 1;
|
||||
|
||||
if(n >= 0)
|
||||
{
|
||||
char* buff = new char[n + 1];
|
||||
|
||||
n = WideCharToMultiByte(CP_UTF8, 0, s, -1, buff, n + 1, NULL, NULL);
|
||||
|
||||
if(n > 0)
|
||||
{
|
||||
ret = std::string(buff);
|
||||
}
|
||||
|
||||
delete [] buff;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DWORD CharSetToCodePage(DWORD charset)
|
||||
{
|
||||
if(charset == CP_UTF8) return CP_UTF8;
|
||||
if(charset == CP_UTF7) return CP_UTF7;
|
||||
|
||||
CHARSETINFO cs = {0};
|
||||
|
||||
TranslateCharsetInfo((DWORD*)charset, &cs, TCI_SRCCHARSET);
|
||||
|
||||
return cs.ciACP;
|
||||
}
|
||||
|
||||
std::string ConvertMBCS(const std::string& s, DWORD src, DWORD dst)
|
||||
{
|
||||
wchar_t* utf16 = new wchar_t[s.size() + 1];
|
||||
|
||||
memset(utf16, 0, (s.size() + 1) * sizeof(wchar_t));
|
||||
|
||||
char* mbcs = new char[s.size() * 6 + 1];
|
||||
|
||||
memset(mbcs, 0, s.size() * 6 + 1);
|
||||
|
||||
int len = MultiByteToWideChar(CharSetToCodePage(src), 0, s.c_str(), s.size(), utf16, (s.size() + 1) * sizeof(wchar_t));
|
||||
|
||||
WideCharToMultiByte(CharSetToCodePage(dst), 0, utf16, len, mbcs, s.size() * 6, NULL, NULL);
|
||||
|
||||
std::string res = mbcs;
|
||||
|
||||
delete [] utf16;
|
||||
delete [] mbcs;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::wstring ConvertMBCS(const std::string& s, DWORD src)
|
||||
{
|
||||
return UTF8To16(ConvertMBCS(s, src, CP_UTF8).c_str());
|
||||
}
|
||||
|
||||
std::wstring CombinePath(LPCWSTR dir, LPCWSTR fn)
|
||||
{
|
||||
wchar_t buff[MAX_PATH];
|
||||
|
||||
PathCombine(buff, dir, fn);
|
||||
|
||||
return std::wstring(buff);
|
||||
}
|
||||
|
||||
std::wstring RemoveFileSpec(LPCWSTR path)
|
||||
{
|
||||
WCHAR buff[MAX_PATH];
|
||||
|
||||
wcscpy(buff, path);
|
||||
|
||||
PathRemoveFileSpec(buff);
|
||||
|
||||
return std::wstring(buff);
|
||||
}
|
||||
|
||||
std::wstring RemoveFileExt(LPCWSTR path)
|
||||
{
|
||||
WCHAR buff[MAX_PATH];
|
||||
|
||||
wcscpy(buff, path);
|
||||
|
||||
PathRemoveExtension(buff);
|
||||
|
||||
return std::wstring(buff);
|
||||
}
|
||||
}
|
87
zfs-win/String.h
Normal file
87
zfs-win/String.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
namespace Util
|
||||
{
|
||||
extern std::wstring Format(const wchar_t* fmt, ...);
|
||||
extern std::string Format(const char* fmt, ...);
|
||||
extern std::wstring TrimLeft(const std::wstring& s, LPCWSTR chrs = L" \t\r\n");
|
||||
extern std::wstring TrimRight(const std::wstring& s, LPCWSTR chrs = L" \t\r\n");
|
||||
extern std::wstring Trim(const std::wstring& s, LPCWSTR chrs = L" \t\r\n");
|
||||
extern std::string TrimLeft(const std::string& s, LPCSTR chrs = " \t\r\n");
|
||||
extern std::string TrimRight(const std::string& s, LPCSTR chrs = " \t\r\n");
|
||||
extern std::string Trim(const std::string& s, LPCSTR chrs = " \t\r\n");
|
||||
extern std::wstring MakeUpper(const std::wstring& s);
|
||||
extern std::wstring MakeLower(const std::wstring& s);
|
||||
extern std::string MakeUpper(const std::string& s);
|
||||
extern std::string MakeLower(const std::string& s);
|
||||
extern std::wstring UTF8To16(LPCSTR s);
|
||||
extern std::string UTF16To8(LPCWSTR s);
|
||||
extern DWORD CharSetToCodePage(DWORD charset);
|
||||
extern std::string ConvertMBCS(const std::string& s, DWORD src, DWORD dst);
|
||||
extern std::wstring ConvertMBCS(const std::string& s, DWORD src);
|
||||
extern std::wstring CombinePath(LPCWSTR dir, LPCWSTR fn);
|
||||
extern std::wstring RemoveFileSpec(LPCWSTR path);
|
||||
extern std::wstring RemoveFileExt(LPCWSTR path);
|
||||
|
||||
template<class T> void Replace(T& s, typename T::const_pointer src, typename T::const_pointer dst)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
int src_size = T(src).size();
|
||||
int dst_size = T(dst).size();
|
||||
|
||||
while((i = s.find(src, i)) != std::string::npos)
|
||||
{
|
||||
s.replace(i, src_size, dst);
|
||||
|
||||
i += dst_size;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, class U, typename SEP> T Explode(const T& str, U& tokens, SEP sep, int limit = 0)
|
||||
{
|
||||
tokens.clear();
|
||||
|
||||
for(int i = 0, j = 0; ; i = j + 1)
|
||||
{
|
||||
j = str.find_first_of(sep, i);
|
||||
|
||||
if(j == T::npos || tokens.size() == limit - 1)
|
||||
{
|
||||
tokens.push_back(Trim(str.substr(i)));
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
tokens.push_back(Trim(str.substr(i, j - i)));
|
||||
}
|
||||
}
|
||||
|
||||
return tokens.front();
|
||||
}
|
||||
|
||||
template<class T, typename SEP> T Implode(const std::list<T>& src, SEP sep)
|
||||
{
|
||||
T s;
|
||||
|
||||
if(!src.empty())
|
||||
{
|
||||
auto i = src.begin();
|
||||
|
||||
for(;;)
|
||||
{
|
||||
s += *i++;
|
||||
|
||||
if(i == src.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
s += sep;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
790
zfs-win/main.cpp
790
zfs-win/main.cpp
@ -22,19 +22,745 @@
|
||||
#include "stdafx.h"
|
||||
#include "Pool.h"
|
||||
#include "DataSet.h"
|
||||
#include <time.h>
|
||||
#include "String.h"
|
||||
#include "../dokan/dokan.h"
|
||||
|
||||
using namespace Util;
|
||||
|
||||
namespace ZFS
|
||||
{
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
Pool pool;
|
||||
DataSet* root;
|
||||
DataSet* mounted;
|
||||
std::wstring name;
|
||||
|
||||
Context() {root = mounted = NULL;}
|
||||
~Context() {delete root;}
|
||||
};
|
||||
|
||||
static void UnixTimeToFileTime(uint64_t t, FILETIME* pft)
|
||||
{
|
||||
// http://support.microsoft.com/kb/167296
|
||||
|
||||
*(uint64_t*)pft = (t * 10000000) + 116444736000000000ull;
|
||||
|
||||
SYSTEMTIME st;
|
||||
FileTimeToSystemTime(pft, &st);
|
||||
t = t;
|
||||
}
|
||||
|
||||
static int WINAPI CreateFile(
|
||||
LPCWSTR FileName,
|
||||
DWORD AccessMode,
|
||||
DWORD ShareMode,
|
||||
DWORD CreationDisposition,
|
||||
DWORD FlagsAndAttributes,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
dnode_phys_t dn;
|
||||
|
||||
memset(&dn, 0, sizeof(dn));
|
||||
|
||||
if(!ctx->mounted->Find(FileName, dn))
|
||||
{
|
||||
dn.type = 0;
|
||||
}
|
||||
|
||||
switch(CreationDisposition)
|
||||
{
|
||||
case CREATE_NEW:
|
||||
return dn.type != 0 ? -ERROR_FILE_EXISTS : -ERROR_ACCESS_DENIED;
|
||||
case CREATE_ALWAYS:
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
case TRUNCATE_EXISTING:
|
||||
return dn.type == 0 ? -ERROR_FILE_NOT_FOUND : -ERROR_ACCESS_DENIED;
|
||||
case OPEN_EXISTING:
|
||||
if(dn.type == 0) return -ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case OPEN_ALWAYS:
|
||||
if(dn.type == 0) return -ERROR_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(dn.type == DMU_OT_DIRECTORY_CONTENTS)
|
||||
{
|
||||
DokanFileInfo->IsDirectory = 1;
|
||||
}
|
||||
|
||||
DokanFileInfo->Context = (ULONG64)new dnode_phys_t(dn);
|
||||
|
||||
return CreationDisposition == OPEN_ALWAYS ? ERROR_ALREADY_EXISTS : 0;
|
||||
}
|
||||
|
||||
static int WINAPI OpenDirectory(
|
||||
LPCWSTR FileName,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
dnode_phys_t dn;
|
||||
|
||||
if(!ctx->mounted->Find(FileName, dn) || dn.type != DMU_OT_DIRECTORY_CONTENTS)
|
||||
{
|
||||
return -ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
DokanFileInfo->Context = (ULONG64)new dnode_phys_t(dn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WINAPI CreateDirectory(
|
||||
LPCWSTR FileName,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI Cleanup(
|
||||
LPCWSTR FileName,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
if(DokanFileInfo->Context != 0)
|
||||
{
|
||||
dnode_phys_t* dn = (dnode_phys_t*)DokanFileInfo->Context;
|
||||
|
||||
delete dn;
|
||||
|
||||
DokanFileInfo->Context = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WINAPI CloseFile(
|
||||
LPCWSTR FileName,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
if(DokanFileInfo->Context != 0)
|
||||
{
|
||||
dnode_phys_t* dn = (dnode_phys_t*)DokanFileInfo->Context;
|
||||
|
||||
delete dn;
|
||||
|
||||
DokanFileInfo->Context = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WINAPI ReadFile(
|
||||
LPCWSTR FileName,
|
||||
LPVOID Buffer,
|
||||
DWORD BufferLength,
|
||||
LPDWORD ReadLength,
|
||||
LONGLONG Offset,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI WriteFile(
|
||||
LPCWSTR FileName,
|
||||
LPCVOID Buffer,
|
||||
DWORD NumberOfBytesToWrite,
|
||||
LPDWORD NumberOfBytesWritten,
|
||||
LONGLONG Offset,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI FlushFileBuffers(
|
||||
LPCWSTR FileName,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI GetFileInformation(
|
||||
LPCWSTR FileName,
|
||||
LPBY_HANDLE_FILE_INFORMATION HandleFileInformation,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
dnode_phys_t* dn = (dnode_phys_t*)DokanFileInfo->Context;
|
||||
|
||||
znode_phys_t* node = (znode_phys_t*)dn->bonus();
|
||||
|
||||
UnixTimeToFileTime(node->crtime[0], &HandleFileInformation->ftCreationTime);
|
||||
UnixTimeToFileTime(node->atime[0], &HandleFileInformation->ftLastAccessTime);
|
||||
UnixTimeToFileTime(node->mtime[0], &HandleFileInformation->ftLastWriteTime);
|
||||
|
||||
HandleFileInformation->dwFileAttributes = 0;
|
||||
|
||||
if(1)
|
||||
{
|
||||
HandleFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_READONLY; // TODO
|
||||
}
|
||||
|
||||
if(dn->type == DMU_OT_DIRECTORY_CONTENTS)
|
||||
{
|
||||
HandleFileInformation->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
|
||||
HandleFileInformation->dwVolumeSerialNumber = 0;
|
||||
HandleFileInformation->nFileSizeLow = (DWORD)(node->size);
|
||||
HandleFileInformation->nFileSizeHigh = (DWORD)(node->size >> 32);
|
||||
HandleFileInformation->nNumberOfLinks = 1;
|
||||
HandleFileInformation->nFileIndexLow = 0;
|
||||
HandleFileInformation->nFileIndexHigh = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WINAPI FindFiles(
|
||||
LPCWSTR FileName,
|
||||
PFillFindData FillFindData,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI FindFilesWithPattern(
|
||||
LPCWSTR PathName,
|
||||
LPCWSTR SearchPattern,
|
||||
PFillFindData Callback, // call this function with PWIN32_FIND_DATAW
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s %s\n", __FUNCTIONW__, PathName, SearchPattern);
|
||||
|
||||
if(DokanFileInfo->Context != 0)
|
||||
{
|
||||
dnode_phys_t* dn = (dnode_phys_t*)DokanFileInfo->Context;
|
||||
|
||||
if(dn->type != DMU_OT_DIRECTORY_CONTENTS)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZFS::ZapObject zap(&ctx->pool);
|
||||
|
||||
if(!zap.Init(dn->blkptr, dn->nblkptr))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(auto i = zap.begin(); i != zap.end(); i++)
|
||||
{
|
||||
uint64_t index = 0;
|
||||
|
||||
if(!zap.Lookup(i->first.c_str(), index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dnode_phys_t subdn;
|
||||
|
||||
if(!ctx->mounted->m_head->Read((size_t)ZFS_DIRENT_OBJ(index), &subdn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
znode_phys_t* node = (znode_phys_t*)subdn.bonus();
|
||||
|
||||
WIN32_FIND_DATAW fd;
|
||||
|
||||
fd.dwFileAttributes = 0;
|
||||
|
||||
if(1)
|
||||
{
|
||||
fd.dwFileAttributes |= FILE_ATTRIBUTE_READONLY; // TODO
|
||||
}
|
||||
|
||||
if(subdn.type == DMU_OT_DIRECTORY_CONTENTS)
|
||||
{
|
||||
fd.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
|
||||
UnixTimeToFileTime(node->crtime[0], &fd.ftCreationTime);
|
||||
UnixTimeToFileTime(node->atime[0], &fd.ftLastAccessTime);
|
||||
UnixTimeToFileTime(node->mtime[0], &fd.ftLastWriteTime);
|
||||
|
||||
fd.nFileSizeLow = (DWORD)(node->size);
|
||||
fd.nFileSizeHigh = (DWORD)(node->size >> 32);
|
||||
|
||||
std::wstring fn = Util::UTF8To16(i->first.c_str());
|
||||
|
||||
wcscpy(fd.cFileName, fn.c_str());
|
||||
|
||||
fd.cAlternateFileName[0] = 0; // ???
|
||||
|
||||
if(Callback(&fd, DokanFileInfo) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WINAPI SetFileAttributes(
|
||||
LPCWSTR FileName,
|
||||
DWORD FileAttributes,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI SetFileTime(
|
||||
LPCWSTR FileName,
|
||||
CONST FILETIME* CreationTime,
|
||||
CONST FILETIME* LastAccessTime,
|
||||
CONST FILETIME* LastWriteTime,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI DeleteFile(
|
||||
LPCWSTR FileName,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI DeleteDirectory(
|
||||
LPCWSTR FileName,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI MoveFile(
|
||||
LPCWSTR FileName, // existing file name
|
||||
LPCWSTR NewFileName,
|
||||
BOOL ReplaceIfExisting,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI SetEndOfFile(
|
||||
LPCWSTR FileName,
|
||||
LONGLONG ByteOffset,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI SetAllocationSize(
|
||||
LPCWSTR FileName,
|
||||
LONGLONG AllocSize,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI LockFile(
|
||||
LPCWSTR FileName,
|
||||
LONGLONG ByteOffset,
|
||||
LONGLONG Length,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI UnlockFile(
|
||||
LPCWSTR FileName,
|
||||
LONGLONG ByteOffset,
|
||||
LONGLONG Length,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s: %s\n", __FUNCTIONW__, FileName);
|
||||
|
||||
return -ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static int WINAPI GetDiskFreeSpace(
|
||||
PULONGLONG FreeBytesAvailable,
|
||||
PULONGLONG TotalNumberOfBytes,
|
||||
PULONGLONG TotalNumberOfFreeBytes,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s\n", __FUNCTIONW__);
|
||||
|
||||
uint64_t total = 0;
|
||||
|
||||
for(auto i = ctx->pool.m_vdevs.begin(); i != ctx->pool.m_vdevs.end(); i++)
|
||||
{
|
||||
VirtualDevice* vdev = *i;
|
||||
|
||||
if(vdev->type == "raidz")
|
||||
{
|
||||
size_t count = vdev->children.size();
|
||||
|
||||
if(count > 1)
|
||||
{
|
||||
total += vdev->asize * (count - vdev->nparity) / count;
|
||||
}
|
||||
}
|
||||
else if(vdev->type == "mirror")
|
||||
{
|
||||
size_t count = vdev->children.size();
|
||||
|
||||
if(count > 0)
|
||||
{
|
||||
total += vdev->asize / count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
total += vdev->asize;
|
||||
}
|
||||
}
|
||||
|
||||
if(TotalNumberOfBytes != NULL)
|
||||
{
|
||||
*TotalNumberOfBytes = total;
|
||||
}
|
||||
|
||||
if(TotalNumberOfFreeBytes != NULL)
|
||||
{
|
||||
*TotalNumberOfFreeBytes = total - ctx->root->m_dir.used_bytes;
|
||||
}
|
||||
|
||||
if(FreeBytesAvailable != NULL)
|
||||
{
|
||||
*FreeBytesAvailable = total - ctx->root->m_dir.used_bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WINAPI GetVolumeInformation(
|
||||
LPWSTR VolumeNameBuffer,
|
||||
DWORD VolumeNameSize,
|
||||
LPDWORD VolumeSerialNumber,
|
||||
LPDWORD MaximumComponentLength,
|
||||
LPDWORD FileSystemFlags,
|
||||
LPWSTR FileSystemNameBuffer,
|
||||
DWORD FileSystemNameSize,
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s\n", __FUNCTIONW__);
|
||||
|
||||
if(VolumeNameBuffer != NULL)
|
||||
{
|
||||
wcscpy(VolumeNameBuffer, ctx->name.c_str());
|
||||
}
|
||||
|
||||
if(VolumeSerialNumber != NULL)
|
||||
{
|
||||
*VolumeSerialNumber = 0;
|
||||
}
|
||||
|
||||
if(MaximumComponentLength != NULL)
|
||||
{
|
||||
*MaximumComponentLength = 256;
|
||||
}
|
||||
|
||||
if(FileSystemFlags != NULL)
|
||||
{
|
||||
*FileSystemFlags =
|
||||
FILE_CASE_SENSITIVE_SEARCH |
|
||||
FILE_CASE_PRESERVED_NAMES |
|
||||
FILE_UNICODE_ON_DISK |
|
||||
FILE_READ_ONLY_VOLUME;
|
||||
}
|
||||
|
||||
if(FileSystemNameBuffer != NULL)
|
||||
{
|
||||
wcscpy(FileSystemNameBuffer, L"ZFS");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WINAPI Unmount(
|
||||
DOKAN_FILE_INFO* DokanFileInfo)
|
||||
{
|
||||
Context* ctx = (Context*)DokanFileInfo->DokanOptions->GlobalContext;
|
||||
|
||||
wprintf(L"%s\n", __FUNCTIONW__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void usage()
|
||||
{
|
||||
printf(
|
||||
"ZFS for Windows\n"
|
||||
"\n"
|
||||
"commands:\n"
|
||||
" mount <drive> <dataset> <pool>\n"
|
||||
" list <pool>\n"
|
||||
"\n"
|
||||
"pool:\n"
|
||||
" Any combination of devices (\\\\.\\PhysicalDriveN) or files.\n"
|
||||
"\n"
|
||||
"examples:\n"
|
||||
" zfs-win.exe mount m \"rpool/ROOT/opensolaris\" \"\\\\.\\PhysicalDrive1\" \"\\\\.\\PhysicalDrive2\"\n"
|
||||
);
|
||||
}
|
||||
|
||||
int _tmain(int argc, _TCHAR* argv[])
|
||||
{
|
||||
// this is just a test, recreating the steps of "ZFS On-Disk Data Walk (Or: Where's My Data)" (google for it)
|
||||
if(argc < 2) {usage(); return -1;}
|
||||
|
||||
wchar_t drive = 0;
|
||||
std::list<std::wstring> paths;
|
||||
/*
|
||||
for(int i = 2; i < argc; i++)
|
||||
std::list<std::wstring> dataset;
|
||||
|
||||
if(wcsicmp(argv[1], L"mount") == 0)
|
||||
{
|
||||
paths.push_back(argv[i]);
|
||||
if(argc < 5) {usage(); return -1;}
|
||||
|
||||
drive = argv[2][0];
|
||||
|
||||
Util::Explode(std::wstring(argv[3]), dataset, L"/");
|
||||
|
||||
for(int i = 4; i < argc; i++)
|
||||
{
|
||||
paths.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
else if(wcsicmp(argv[1], L"list") == 0)
|
||||
{
|
||||
if(argc < 3) {usage(); return -1;}
|
||||
|
||||
for(int i = 2; i < argc; i++)
|
||||
{
|
||||
paths.push_back(argv[i]);
|
||||
}
|
||||
|
||||
return -1; // TODO
|
||||
}
|
||||
|
||||
if(paths.empty()) {usage(); return -1;}
|
||||
|
||||
/*
|
||||
|
||||
paths.clear();
|
||||
|
||||
paths.push_back(L"\\\\.\\PhysicalDrive1");
|
||||
paths.push_back(L"\\\\.\\PhysicalDrive2");
|
||||
paths.push_back(L"\\\\.\\PhysicalDrive3");
|
||||
paths.push_back(L"\\\\.\\PhysicalDrive4");
|
||||
|
||||
Util::Explode(std::wstring(L"share/backup"), dataset, L"/");
|
||||
|
||||
*/
|
||||
|
||||
ZFS::Context ctx;
|
||||
|
||||
std::wstring name = dataset.front();
|
||||
|
||||
dataset.pop_front();
|
||||
|
||||
if(!ctx.pool.Open(paths, UTF16To8(name.c_str()).c_str()))
|
||||
{
|
||||
printf("Failed to open pool\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx.root = new ZFS::DataSet(&ctx.pool);
|
||||
|
||||
if(!ctx.root->Init(&ctx.pool.m_devs.front()->m_active->rootbp, 1))
|
||||
{
|
||||
printf("Failed to read root dataset\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZFS::DataSet* ds = ctx.root;
|
||||
|
||||
ctx.name = name;
|
||||
|
||||
while(!dataset.empty())
|
||||
{
|
||||
name = dataset.front();
|
||||
|
||||
dataset.pop_front();
|
||||
|
||||
ZFS::DataSet* ds2 = NULL;
|
||||
|
||||
for(auto i = ds->m_children.begin(); i != ds->m_children.end(); i++)
|
||||
{
|
||||
if((*i)->m_name == UTF16To8(name.c_str()))
|
||||
{
|
||||
ds2 = *i;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ds2 == NULL) {usage(); return -1;}
|
||||
|
||||
ds = ds2;
|
||||
|
||||
// ctx.name += L"/" + name;
|
||||
}
|
||||
|
||||
ctx.mounted = ds;
|
||||
|
||||
//
|
||||
|
||||
DOKAN_OPTIONS options;
|
||||
|
||||
memset(&options, 0, sizeof(DOKAN_OPTIONS));
|
||||
|
||||
options.DriveLetter = drive;
|
||||
options.GlobalContext = (ULONG64)&ctx;
|
||||
options.ThreadCount = 1; // TODO
|
||||
options.Options = DOKAN_OPTION_KEEP_ALIVE;
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
options.Options |= DOKAN_OPTION_DEBUG;
|
||||
|
||||
#endif
|
||||
|
||||
DOKAN_OPERATIONS ops;
|
||||
|
||||
memset(&ops, 0, sizeof(DOKAN_OPERATIONS));
|
||||
|
||||
ops.CreateFile = ZFS::CreateFile;
|
||||
ops.OpenDirectory = ZFS::OpenDirectory;
|
||||
ops.CreateDirectory = ZFS::CreateDirectory;
|
||||
ops.Cleanup = ZFS::Cleanup;
|
||||
ops.CloseFile = ZFS::CloseFile;
|
||||
ops.ReadFile = ZFS::ReadFile;
|
||||
ops.WriteFile = ZFS::WriteFile;
|
||||
ops.FlushFileBuffers = ZFS::FlushFileBuffers;
|
||||
ops.GetFileInformation = ZFS::GetFileInformation;
|
||||
ops.FindFiles = ZFS::FindFiles;
|
||||
ops.FindFilesWithPattern = ZFS::FindFilesWithPattern;
|
||||
ops.SetFileAttributes = ZFS::SetFileAttributes;
|
||||
ops.SetFileTime = ZFS::SetFileTime;
|
||||
ops.DeleteFile = ZFS::DeleteFile;
|
||||
ops.DeleteDirectory = ZFS::DeleteDirectory;
|
||||
ops.MoveFile = ZFS::MoveFile;
|
||||
ops.SetEndOfFile = ZFS::SetEndOfFile;
|
||||
ops.SetAllocationSize = ZFS::SetAllocationSize;
|
||||
ops.LockFile = ZFS::LockFile;
|
||||
ops.UnlockFile = ZFS::UnlockFile;
|
||||
ops.GetDiskFreeSpace = ZFS::GetDiskFreeSpace;
|
||||
ops.GetVolumeInformation = ZFS::GetVolumeInformation;
|
||||
ops.Unmount = ZFS::Unmount;
|
||||
|
||||
switch(int status = DokanMain(&options, &ops))
|
||||
{
|
||||
case DOKAN_SUCCESS:
|
||||
printf("Success\n");
|
||||
break;
|
||||
case DOKAN_ERROR:
|
||||
printf("Error\n");
|
||||
break;
|
||||
case DOKAN_DRIVE_LETTER_ERROR:
|
||||
printf("Bad Drive letter\n");
|
||||
break;
|
||||
case DOKAN_DRIVER_INSTALL_ERROR:
|
||||
printf("Can't install driver\n");
|
||||
break;
|
||||
case DOKAN_START_ERROR:
|
||||
printf("Driver something wrong\n");
|
||||
break;
|
||||
case DOKAN_MOUNT_ERROR:
|
||||
printf("Can't assign a drive letter\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown error: %d\n", status);
|
||||
break;
|
||||
}
|
||||
|
||||
// std::list<std::wstring> paths;
|
||||
|
||||
/*
|
||||
const char* name = "mpool";
|
||||
|
||||
@ -48,6 +774,7 @@ int _tmain(int argc, _TCHAR* argv[])
|
||||
paths.push_back(L"D:\\Virtual Machines\\ZFSVM\\ZFSVM7-flat.vmdk");
|
||||
paths.push_back(L"D:\\Virtual Machines\\ZFSVM\\ZFSVM8-flat.vmdk");
|
||||
*/
|
||||
|
||||
/*
|
||||
const char* name = "share";
|
||||
|
||||
@ -56,57 +783,18 @@ int _tmain(int argc, _TCHAR* argv[])
|
||||
paths.push_back(L"\\\\.\\PhysicalDrive3");
|
||||
paths.push_back(L"\\\\.\\PhysicalDrive4");
|
||||
*/
|
||||
/**/
|
||||
|
||||
/*
|
||||
const char* name = "rpool";
|
||||
|
||||
paths.push_back(L"D:\\Virtual Machines\\OpenSolaris\\OpenSolaris-flat.vmdk");
|
||||
|
||||
ZFS::Pool pool;
|
||||
*/
|
||||
|
||||
if(!pool.Open(name, paths))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
const char* name = "rpool";
|
||||
|
||||
ZFS::Device* dev = pool.m_devs.front();
|
||||
|
||||
ZFS::ObjectSet os(&pool);
|
||||
|
||||
if(os.Init(&dev->m_active->rootbp, 1))
|
||||
{
|
||||
ZFS::DataSet ds(&pool);
|
||||
|
||||
if(ds.Init(os))
|
||||
{
|
||||
std::list<ZFS::DataSet*> mpl;
|
||||
|
||||
ds.GetMountPoints(mpl);
|
||||
|
||||
for(auto i = mpl.begin(); i != mpl.end(); i++)
|
||||
{
|
||||
if((*i)->m_mountpoint != "/") continue;
|
||||
|
||||
ZFS::ObjectSet os(&pool);
|
||||
|
||||
if(os.Init(&(*i)->m_dataset.bp, 1))
|
||||
{
|
||||
dnode_phys_t root;
|
||||
|
||||
if(os.Read("ROOT", &root, DMU_OT_DIRECTORY_CONTENTS))
|
||||
{
|
||||
znode_phys_t* node = (znode_phys_t*)root.bonus();
|
||||
|
||||
ZFS::ZapObject zap(&pool);
|
||||
|
||||
if(zap.Init(root.blkptr, root.nblkptr))
|
||||
{
|
||||
int i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
paths.push_back(L"D:\\Virtual Machines\\ZFS1VM\\ZFS1VM-flat.vmdk");
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
@ -50,7 +50,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>$(OutDir)zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>$(OutDir)zlib.lib;$(SolutionDir)dokan\dokan.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@ -60,7 +60,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>$(OutDir)zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>$(OutDir)zlib.lib;$(SolutionDir)dokan\dokan.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@ -72,6 +72,7 @@
|
||||
<ClInclude Include="NameValueList.h" />
|
||||
<ClInclude Include="ObjectSet.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="String.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="Pool.h" />
|
||||
<ClInclude Include="ZapObject.h" />
|
||||
@ -91,6 +92,7 @@
|
||||
<ClCompile Include="Pool.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="Hash.cpp" />
|
||||
<ClCompile Include="String.cpp" />
|
||||
<ClCompile Include="ZapObject.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
@ -51,6 +51,9 @@
|
||||
<ClInclude Include="DataSet.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="String.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
@ -86,5 +89,8 @@
|
||||
<ClCompile Include="DataSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="String.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -963,8 +963,8 @@ struct zfs_acl_phys_t
|
||||
* 12 bits are unused.
|
||||
*/
|
||||
|
||||
#define ZFS_DIRENT_TYPE(de) BF64_GET(de, 60, 4)
|
||||
#define ZFS_DIRENT_OBJ(de) BF64_GET(de, 0, 48)
|
||||
#define ZFS_DIRENT_TYPE(de) ((de) >> 60)
|
||||
#define ZFS_DIRENT_OBJ(de) ((de) & 0xFFFFFFFFFFFFull)
|
||||
|
||||
/*
|
||||
* This is the persistent portion of the znode. It is stored
|
||||
|
Loading…
x
Reference in New Issue
Block a user