Merge branch 'dirstream'

This commit is contained in:
Andrea Mazzoleni 2014-10-04 16:41:39 +02:00
commit 84552244ba
16 changed files with 607 additions and 244 deletions

View file

@ -4,6 +4,7 @@ SnapRAID HISTORY
7.0 2014/9
==========
* Adds AVX2 support to improve parity generation speed.
* Faster directory scanning for Windows.
* Prints the time spent waiting for each disk also in 'scrub'.
* The CPU usage, speed and ETA estimations are now based on the last 100
seconds rather than from the start.

View file

@ -225,7 +225,7 @@ endif
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS) --gen-conf bench/content
echo --- Filter
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) -d disk1 check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) -f "*a" -f "a/" -f "/b/a*" check
$(TESTENV) ./snapraid$(EXEEXT) $(CHECKFLAGS_VERBOSE) -c $(CONF) -f \\*a -f a/ -f /b/a\\* check
echo --- Delete some files and sync
rm bench/disk4/a/9*
rm bench/disk5/a/9*

2
TODO
View file

@ -15,6 +15,8 @@ same timestamp/size are present.
* In the log print file name as escaped to avoid that internal ":" and "\n"
get misinterpreted.
* Shall CreateFileW use FILE_SHARE_DELETE ?
- Minor
* Provides a way to rename disks in content file.

View file

@ -377,7 +377,7 @@ void deleted_free(struct snapraid_deleted* deleted)
free(deleted);
}
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical)
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical)
{
struct snapraid_file* file;
block_off_t i;

View file

@ -643,7 +643,7 @@ static inline void file_flag_clear(struct snapraid_file* file, unsigned mask)
/**
* Allocates a file.
*/
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, uint64_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical);
struct snapraid_file* file_alloc(unsigned block_size, const char* sub, data_off_t size, uint64_t mtime_sec, int mtime_nsec, uint64_t inode, uint64_t physical);
/**
* Deallocates a file.

View file

@ -265,6 +265,17 @@ static void import_dir(struct snapraid_state* state, const char* dir)
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &st);
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows */
if (st.st_mode == 0) {
if (lstat(path, &st) != 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error in stat file/directory '%s'. %s.\n", path, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#else
/* get lstat info about the file */
if (lstat(path, &st) != 0) {

View file

@ -32,7 +32,27 @@ typedef struct _FILE_ATTRIBUTE_TAG_INFO {
DWORD FileAttributes;
DWORD ReparseTag;
} FILE_ATTRIBUTE_TAG_INFO;
typedef struct _FILE_ID_BOTH_DIR_INFO {
DWORD NextEntryOffset;
DWORD FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
DWORD FileAttributes;
DWORD FileNameLength;
DWORD EaSize;
CCHAR ShortNameLength;
WCHAR ShortName[12];
LARGE_INTEGER FileId;
WCHAR FileName[1];
} FILE_ID_BOTH_DIR_INFO;
#define FileAttributeTagInfo 9
#define FileIdBothDirectoryInfo 10
#ifndef IO_REPARSE_TAG_DEDUP
#define IO_REPARSE_TAG_DEDUP (0x80000013)
@ -99,10 +119,17 @@ static char* last_error;
*/
static int is_wine;
void os_init(void)
/**
* If we should use the legacy FindFirst/Next way to list directories.
*/
static int is_scan_winfind;
void os_init(int opt)
{
HMODULE kernel32, ntdll;
is_scan_winfind = opt != 0;
kernel32 = GetModuleHandle("KERNEL32.DLL");
if (!kernel32) {
fprintf(stderr, "Error loading the KERNEL32 module.\n");
@ -184,26 +211,7 @@ static wchar_t* u8tou16(const char* src)
/**
* Converts a generic string from UTF16 to UTF8.
*/
static char* u16tou8(const wchar_t* src)
{
int ret;
if (++conv_utf8 == CONV_ROLL)
conv_utf8 = 0;
ret = WideCharToMultiByte(CP_UTF8, 0, src, -1, conv_utf8_buffer[conv_utf8], sizeof(conv_utf8_buffer[0]), 0, 0);
if (ret <= 0) {
fwprintf(stderr, L"Error converting name %s from UTF-16 to UTF-8\n", src);
exit(EXIT_FAILURE);
}
return conv_utf8_buffer[conv_utf8];
}
/**
* Converts a generic string from UTF16 to UTF8.
*/
static char* u16tou8n(const wchar_t* src, size_t number_of_wchar, size_t* result_length_without_terminator)
static char* u16tou8(const wchar_t* src, size_t number_of_wchar, size_t* result_length_without_terminator)
{
int ret;
@ -212,7 +220,7 @@ static char* u16tou8n(const wchar_t* src, size_t number_of_wchar, size_t* result
ret = WideCharToMultiByte(CP_UTF8, 0, src, number_of_wchar, conv_utf8_buffer[conv_utf8], sizeof(conv_utf8_buffer[0]), 0, 0);
if (ret <= 0) {
fwprintf(stderr, L"Error converting from UTF-16 to UTF-8\n");
fprintf(stderr, "Error converting from UTF-16 to UTF-8\n");
exit(EXIT_FAILURE);
}
@ -340,27 +348,31 @@ static BOOL GetReparseTagInfoByHandle(HANDLE hFile, FILE_ATTRIBUTE_TAG_INFO* lpF
}
/**
* Converts Windows info to the Unix stat format.
* Converts Windows attr to the Unix stat format.
*/
static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ATTRIBUTE_TAG_INFO* tag, struct windows_stat* st)
static void windows_attr2stat(DWORD FileAttributes, DWORD ReparseTag, struct windows_stat* st)
{
uint64_t mtime;
/* Convert special attributes */
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) {
if ((FileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) {
st->st_mode = S_IFBLK;
st->st_desc = "device";
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) != 0) { /* Offline */
} else if ((FileAttributes & FILE_ATTRIBUTE_OFFLINE) != 0) { /* Offline */
st->st_mode = S_IFCHR;
st->st_desc = "offline";
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) != 0) { /* Temporary */
} else if ((FileAttributes & FILE_ATTRIBUTE_TEMPORARY) != 0) { /* Temporary */
st->st_mode = S_IFCHR;
st->st_desc = "temporary";
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { /* Reparse point */
switch (tag->ReparseTag) {
/* For deduplicated files, assume that they are regular ones */
} else if ((FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { /* Reparse point */
switch (ReparseTag) {
/* if we don't have the ReparseTag information */
case 0 :
/* don't set the st_mode, to set it later calling lstat_sync() */
st->st_mode = 0;
st->st_desc = "unknown";
break;
/* for deduplicated files, assume that they are regular ones */
case IO_REPARSE_TAG_DEDUP :
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFDIR;
st->st_desc = "directory-dedup";
} else {
@ -369,7 +381,7 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE
}
break;
case IO_REPARSE_TAG_SYMLINK :
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFLNKDIR;
st->st_desc = "reparse-point-symlink-dir";
} else {
@ -377,11 +389,11 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE
st->st_desc = "reparse-point-symlink-file";
}
break;
/* All the other are skipped as reparse-point */
/* all the other are skipped as reparse-point */
case IO_REPARSE_TAG_MOUNT_POINT :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point-mount";
break;
break;
case IO_REPARSE_TAG_NFS :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point-nfs";
@ -391,8 +403,8 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE
st->st_desc = "reparse-point";
break;
}
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0) { /* System */
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
} else if ((FileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0) { /* System */
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFCHR;
st->st_desc = "system-directory";
} else {
@ -400,7 +412,7 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE
st->st_desc = "system-file";
}
} else {
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
if ((FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFDIR;
st->st_desc = "directory";
} else {
@ -409,8 +421,18 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE
}
}
/* Store the HIDDEN attribute in a separate field */
st->st_hidden = (info->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
/* store the HIDDEN attribute in a separate field */
st->st_hidden = (FileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
}
/**
* Converts Windows info to the Unix stat format.
*/
static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ATTRIBUTE_TAG_INFO* tag, struct windows_stat* st)
{
uint64_t mtime;
windows_attr2stat(info->dwFileAttributes, tag->ReparseTag, st);
st->st_size = info->nFileSizeHigh;
st->st_size <<= 32;
@ -434,9 +456,44 @@ static void windows_info2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE
st->st_ino <<= 32;
st->st_ino |= info->nFileIndexLow;
st->st_nlink = info->nNumberOfLinks;
st->st_dev = info->dwVolumeSerialNumber;
/* GetFileInformationByHandle() ensures to return synced information */
st->st_sync = 1;
}
/**
* Converts Windows info to the Unix stat format.
*/
static void windows_stream2stat(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ID_BOTH_DIR_INFO* stream, struct windows_stat* st)
{
uint64_t mtime;
/* The FILE_ID_BOTH_DIR_INFO doesn't have the ReparseTag information */
/* we could use instead FILE_ID_EXTD_DIR_INFO, but it's available only */
/* from Windows Server 2012 */
windows_attr2stat(stream->FileAttributes, 0, st);
st->st_size = stream->EndOfFile.QuadPart;
mtime = stream->LastWriteTime.QuadPart;
/*
* Convert to unix time
*
* How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME
* http://support.microsoft.com/kb/167296
*/
mtime -= 116444736000000000LL;
st->st_mtime = mtime / 10000000;
st->st_mtimensec = (mtime % 10000000) * 100;
st->st_ino = stream->FileId.QuadPart;
st->st_dev = info->dwVolumeSerialNumber;
/* directory listing doesn't ensure to return synced information */
st->st_sync = 0;
}
/**
@ -446,71 +503,7 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s
{
uint64_t mtime;
/* Convert special attributes */
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) {
st->st_mode = S_IFBLK;
st->st_desc = "device";
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) != 0) { /* Offline */
st->st_mode = S_IFCHR;
st->st_desc = "offline";
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) != 0) { /* Temporary */
st->st_mode = S_IFCHR;
st->st_desc = "temporary";
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { /* Reparse point */
switch (info->dwReserved0) {
/* For deduplicated files, assume that they are regular ones */
case IO_REPARSE_TAG_DEDUP :
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFDIR;
st->st_desc = "directory-dedup";
} else {
st->st_mode = S_IFREG;
st->st_desc = "regular-dedup";
}
break;
case IO_REPARSE_TAG_SYMLINK :
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFLNKDIR;
st->st_desc = "reparse-point-symlink-dir";
} else {
st->st_mode = S_IFLNK;
st->st_desc = "reparse-point-symlink-file";
}
break;
/* All the other are skipped as reparse-point */
case IO_REPARSE_TAG_MOUNT_POINT :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point-mount";
break;
case IO_REPARSE_TAG_NFS :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point-nfs";
break;
default :
st->st_mode = S_IFCHR;
st->st_desc = "reparse-point";
break;
}
} else if ((info->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0) { /* System */
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFCHR;
st->st_desc = "system-directory";
} else {
st->st_mode = S_IFREG;
st->st_desc = "system-file";
}
} else {
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
st->st_mode = S_IFDIR;
st->st_desc = "directory";
} else {
st->st_mode = S_IFREG;
st->st_desc = "regular";
}
}
/* Store the HIDDEN attribute in a separate field */
st->st_hidden = (info->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
windows_attr2stat(info->dwFileAttributes, info->dwReserved0, st);
st->st_size = info->nFileSizeHigh;
st->st_size <<= 32;
@ -533,11 +526,11 @@ static void windows_finddata2stat(const WIN32_FIND_DATAW* info, struct windows_s
/* No inode information available */
st->st_ino = 0;
/* No link information available */
st->st_nlink = 0;
/* No device information available */
st->st_dev = 0;
/* directory listing doesn't ensure to return synced information */
st->st_sync = 0;
}
static void windows_finddata2dirent(const WIN32_FIND_DATAW* info, struct windows_dirent* dirent)
@ -545,19 +538,35 @@ static void windows_finddata2dirent(const WIN32_FIND_DATAW* info, struct windows
const char* name;
size_t len;
name = u16tou8(info->cFileName);
len = strlen(name);
name = u16tou8(info->cFileName, wcslen(info->cFileName), &len);
if (len + 1 >= sizeof(dirent->d_name)) {
fprintf(stderr, "Name too long\n");
exit(EXIT_FAILURE);
}
memcpy(dirent->d_name, name, len + 1);
memcpy(dirent->d_name, name, len);
dirent->d_name[len] = 0;
/* Store the HIDDEN attribute in a separate field */
dirent->d_hidden = (info->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
windows_finddata2stat(info, &dirent->d_stat);
}
static void windows_stream2dirent(const BY_HANDLE_FILE_INFORMATION* info, const FILE_ID_BOTH_DIR_INFO* stream, struct windows_dirent* dirent)
{
const char* name;
size_t len;
name = u16tou8(stream->FileName, stream->FileNameLength / 2, &len);
if (len + 1 >= sizeof(dirent->d_name)) {
fprintf(stderr, "Name too long\n");
exit(EXIT_FAILURE);
}
memcpy(dirent->d_name, name, len);
dirent->d_name[len] = 0;
windows_stream2stat(info, stream, &dirent->d_stat);
}
/**
@ -658,7 +667,7 @@ int windows_lstat(const char* file, struct windows_stat* st)
void windows_dirent_lstat(const struct windows_dirent* dd, struct windows_stat* st)
{
windows_finddata2stat(&dd->d_data, st);
memcpy(st, &dd->d_stat, sizeof(struct windows_stat));
}
int windows_mkdir(const char* file)
@ -681,7 +690,7 @@ int windows_rmdir(const char* file)
return 0;
}
int lstat_ex(const char* file, struct windows_stat* st)
int lstat_sync(const char* file, struct windows_stat* st)
{
BY_HANDLE_FILE_INFORMATION info;
FILE_ATTRIBUTE_TAG_INFO tag;
@ -931,7 +940,22 @@ int windows_open(const char* file, int flags, ...)
return ret;
}
windows_dir* windows_opendir(const char* dir)
struct windows_dir_struct {
BY_HANDLE_FILE_INFORMATION info;
WIN32_FIND_DATAW find;
HANDLE h;
struct windows_dirent entry;
unsigned char* buffer;
unsigned buffer_size;
unsigned buffer_pos;
int state;
};
#define DIR_STATE_EOF -1 /**< End of the dir stream */
#define DIR_STATE_EMPTY 0 /**< The entry is empty. */
#define DIR_STATE_FILLED 1 /**< The entry is valid. */
static windows_dir* windows_opendir_find(const char* dir)
{
wchar_t* wdir;
windows_dir* dirstream;
@ -952,12 +976,12 @@ windows_dir* windows_opendir(const char* dir)
wdir[len++] = L'*';
wdir[len++] = 0;
dirstream->h = FindFirstFileW(wdir, &dirstream->buffer.d_data);
dirstream->h = FindFirstFileW(wdir, &dirstream->find);
if (dirstream->h == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND) {
dirstream->flags = -1; /* empty dir */
dirstream->state = DIR_STATE_EOF;
return dirstream;
}
@ -966,47 +990,48 @@ windows_dir* windows_opendir(const char* dir)
return 0;
}
dirstream->flags = 1;
windows_finddata2dirent(&dirstream->buffer.d_data, &dirstream->buffer);
windows_finddata2dirent(&dirstream->find, &dirstream->entry);
dirstream->state = DIR_STATE_FILLED;
return dirstream;
}
struct windows_dirent* windows_readdir(windows_dir* dirstream)
static struct windows_dirent* windows_readdir_find(windows_dir* dirstream)
{
if (dirstream->flags == -1) {
errno = 0; /* end of stream */
return 0;
}
if (dirstream->state == DIR_STATE_EMPTY) {
if (!FindNextFileW(dirstream->h, &dirstream->find)) {
DWORD error = GetLastError();
if (dirstream->flags == 1) {
dirstream->flags = 0;
return &dirstream->buffer;
}
if (error != ERROR_NO_MORE_FILES) {
windows_errno(error);
return 0;
}
if (!FindNextFileW(dirstream->h, &dirstream->buffer.d_data)) {
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
errno = 0; /* end of stream */
return 0;
dirstream->state = DIR_STATE_EOF;
} else {
windows_finddata2dirent(&dirstream->find, &dirstream->entry);
dirstream->state = DIR_STATE_FILLED;
}
windows_errno(error);
return 0;
}
windows_finddata2dirent(&dirstream->buffer.d_data, &dirstream->buffer);
if (dirstream->state == DIR_STATE_FILLED) {
dirstream->state = DIR_STATE_EMPTY;
return &dirstream->entry;
}
return &dirstream->buffer;
/* otherwise it's the end of stream */
assert(dirstream->state == DIR_STATE_EOF);
errno = 0;
return 0;
}
int windows_closedir(windows_dir* dirstream)
static int windows_closedir_find(windows_dir* dirstream)
{
if (dirstream->h != INVALID_HANDLE_VALUE) {
if (!FindClose(dirstream->h)) {
DWORD error = GetLastError();
free(dirstream);
windows_errno(error);
@ -1019,9 +1044,177 @@ int windows_closedir(windows_dir* dirstream)
return 0;
}
static int windows_first_stream(windows_dir* dirstream)
{
FILE_ID_BOTH_DIR_INFO* fd;
if (!ptr_GetFileInformationByHandleEx(dirstream->h, FileIdBothDirectoryInfo, dirstream->buffer, dirstream->buffer_size)) {
DWORD error = GetLastError();
if (error == ERROR_NO_MORE_FILES) {
dirstream->state = DIR_STATE_EOF;
return 0;
}
windows_errno(error);
return -1;
}
/* get the first entry */
dirstream->buffer_pos = 0;
fd = (FILE_ID_BOTH_DIR_INFO*)dirstream->buffer;
windows_stream2dirent(&dirstream->info, fd, &dirstream->entry);
dirstream->state = DIR_STATE_FILLED;
return 0;
}
static int windows_next_stream(windows_dir* dirstream)
{
FILE_ID_BOTH_DIR_INFO* fd;
/* last entry read */
fd = (FILE_ID_BOTH_DIR_INFO*)(dirstream->buffer + dirstream->buffer_pos);
/* check if there is a next one */
if (fd->NextEntryOffset == 0) {
/* if not, fill it up again */
if (windows_first_stream(dirstream) != 0)
return -1;
return 0;
}
/* go to the next one */
dirstream->buffer_pos += fd->NextEntryOffset;
fd = (FILE_ID_BOTH_DIR_INFO*)(dirstream->buffer + dirstream->buffer_pos);
windows_stream2dirent(&dirstream->info, fd, &dirstream->entry);
dirstream->state = DIR_STATE_FILLED;
return 0;
}
static windows_dir* windows_opendir_stream(const char* dir)
{
windows_dir* dirstream;
WCHAR* wdir;
dirstream = malloc(sizeof(windows_dir));
if (!dirstream) {
fprintf(stderr, "Low memory\n");
exit(EXIT_FAILURE);
}
wdir = convert(dir);
/* uses a 64 kB buffer for reading directory */
dirstream->buffer_size = 64 * 1024;
dirstream->buffer = malloc(dirstream->buffer_size);
if (!dirstream->buffer) {
fprintf(stderr, "Low memory\n");
exit(EXIT_FAILURE);
}
dirstream->h = CreateFileW(wdir, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (dirstream->h == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
free(dirstream->buffer);
free(dirstream);
windows_errno(error);
return 0;
}
/* get dir information for the VolumeSerialNumber */
/* this value is used for all the files in the dir */
if (!GetFileInformationByHandle(dirstream->h, &dirstream->info)) {
DWORD error = GetLastError();
CloseHandle(dirstream->h);
free(dirstream->buffer);
free(dirstream);
windows_errno(error);
return 0;
}
if (windows_first_stream(dirstream) != 0) {
CloseHandle(dirstream->h);
free(dirstream->buffer);
free(dirstream);
return 0;
}
return dirstream;
}
static struct windows_dirent* windows_readdir_stream(windows_dir* dirstream)
{
if (dirstream->state == DIR_STATE_EMPTY) {
if (windows_next_stream(dirstream) != 0) {
free(dirstream->buffer);
free(dirstream);
return 0;
}
}
if (dirstream->state == DIR_STATE_FILLED) {
dirstream->state = DIR_STATE_EMPTY;
return &dirstream->entry;
}
/* otherwise it's the end of stream */
assert(dirstream->state == DIR_STATE_EOF);
errno = 0;
return 0;
}
static int windows_closedir_stream(windows_dir* dirstream)
{
if (dirstream->h != INVALID_HANDLE_VALUE) {
if (!CloseHandle(dirstream->h)) {
DWORD error = GetLastError();
free(dirstream->buffer);
free(dirstream);
windows_errno(error);
return -1;
}
}
free(dirstream->buffer);
free(dirstream);
return 0;
}
windows_dir* windows_opendir(const char* dir)
{
/* if we have GetFileInformationByHandleEx() we can read */
/* the directory using a stream */
if (!is_scan_winfind && ptr_GetFileInformationByHandleEx != 0)
return windows_opendir_stream(dir);
else
return windows_opendir_find(dir);
}
struct windows_dirent* windows_readdir(windows_dir* dirstream)
{
if (!is_scan_winfind && ptr_GetFileInformationByHandleEx != 0)
return windows_readdir_stream(dirstream);
else
return windows_readdir_find(dirstream);
}
int windows_closedir(windows_dir* dirstream)
{
if (!is_scan_winfind && ptr_GetFileInformationByHandleEx != 0)
return windows_closedir_stream(dirstream);
else
return windows_closedir_find(dirstream);
}
int windows_dirent_hidden(struct dirent* dd)
{
return dd->d_hidden;
return dd->d_stat.st_hidden;
}
const char* windows_stat_desc(struct stat* st)
@ -1134,7 +1327,7 @@ int windows_readlink(const char* file, char* buffer, size_t size)
}
/* convert the name to UTF-8 */
name = u16tou8n(rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.PrintNameOffset,
name = u16tou8(rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.PrintNameOffset,
rdb->SymbolicLinkReparseBuffer.PrintNameLength / 2, &len);
/* check for overflow */
@ -1155,7 +1348,7 @@ int devuuid(uint64_t device, char* uuid, size_t uuid_size)
return 0;
}
int filephy(const char* file, struct stat* st, uint64_t* physical)
int filephy(const char* file, uint64_t size, uint64_t* physical)
{
HANDLE h;
STARTING_VCN_INPUT_BUFFER svib;
@ -1164,9 +1357,11 @@ int filephy(const char* file, struct stat* st, uint64_t* physical)
BOOL ret;
DWORD n;
/* in Wine just uses the inode number because FSCTL_GET_RETRIVIAL_POINTERS is not supported */
(void)size;
/* in Wine FSCTL_GET_RETRIVIAL_POINTERS is not supported */
if (is_wine) {
*physical = st->st_ino;
*physical = FILEPHY_UNREPORTED_OFFSET;
return 0;
}

View file

@ -80,6 +80,7 @@
#define dirent_hidden windows_dirent_hidden
#define HAVE_STRUCT_DIRENT_D_STAT 1
#undef HAVE_STRUCT_DIRENT_D_INO
#undef HAVE_STRUCT_STAT_ST_NLINK
#define dirent_lstat windows_dirent_lstat
#define stat_desc windows_stat_desc
#undef sleep
@ -130,23 +131,26 @@ struct windows_stat {
int64_t st_mtime;
int32_t st_mtimensec;
uint32_t st_mode;
uint32_t st_nlink;
uint32_t st_dev;
int st_hidden;
const char* st_desc;
int st_sync; /**< If the information are in sync with the filesystem. */
};
/**
* Like the C fstat() including the inode/device information.
* Like the C fstat().
*/
int windows_fstat(int fd, struct windows_stat* st);
/**
* Like the C lstat() but without the inode information.
* In Windows the inode information is not reported.
* In Windows and in case of hardlinks, the size and the attributes of the file can
* be completely bogus, because changes made by other hardlinks are reported in the
* directory entry only when the file is opened.
* Like the C lstat(), but with some limitations.
*
* The st_ino field may be 0 if it's not possible to read it in a fast way.
* Specifically this always happens.
*
* In case of hardlinks, the size and the attributes of the file can
* be completely bogus, because changes made by other hardlinks are reported
* in the directory entry only when the file is opened.
*
* MSDN CreateHardLinks
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363860%28v=vs.85%29.aspx
@ -159,12 +163,12 @@ int windows_fstat(int fd, struct windows_stat* st);
* 'However, the directory entry size and attribute information is updated only'
* 'for the link through which the change was made.'
*
* Use lstat_ex() to override these limitations.
* Use lstat_sync() to override these limitations.
*/
int windows_lstat(const char* file, struct windows_stat* st);
/**
* Like the C stat() including the inode/device information.
* Like the C stat().
*/
int windows_stat(const char* file, struct windows_stat* st);
@ -179,12 +183,18 @@ int windows_mkdir(const char* file);
int windows_rmdir(const char* file);
/**
* Like the C lstat() including the inode/device information.
* It doesn't work for all kind of files and directories. For example "\System Volume Information" cannot be opened.
* Like the C lstat(), but with some limitations.
*
* This call fills all the st_* fields of the stat struct.
*
* It doesn't work for all kinds of files and directories.
* You must call it only for regular files.
* For example, "\System Volume Information" cannot be accessed.
*
* Note that instead lstat() works for all the files.
*/
#define HAVE_LSTAT_EX 1
int lstat_ex(const char* file, struct windows_stat* st);
#define HAVE_LSTAT_SYNC 1
int lstat_sync(const char* file, struct windows_stat* st);
/**
* Like the C ftruncate().
@ -238,18 +248,13 @@ int windows_open(const char* file, int flags, ...);
*/
struct windows_dirent {
char d_name[PATH_MAX];
int d_hidden;
WIN32_FIND_DATAW d_data;
struct windows_stat d_stat;
};
/**
* Like the C DIR.
*/
struct windows_dir_struct {
HANDLE h;
struct windows_dirent buffer;
int flags;
};
struct windows_dir_struct;
typedef struct windows_dir_struct windows_dir;
/**
@ -268,8 +273,17 @@ struct windows_dirent* windows_readdir(windows_dir* dirstream);
int windows_closedir(windows_dir* dirstream);
/**
* Convert a dirent record to a lstat record.
* Just like the one obtained calling lstat().
* Convert a dirent record to a lstat record, but with some limitations.
*
* The st_mode field may be 0 if the file is a reparse point.
* Specifically this happens if we are using GetFileInformationByHandleEx()
* to read the directory stream.
*
* The st_ino field may be 0 if it's not possible to read it in a fast way.
* Specifically this happens if we are using FindFirst/FindNext to enumerate
* the directory.
*
* In such cases, call lstat_sync() to fill the missing fields.
*/
void windows_dirent_lstat(const struct windows_dirent* dd, struct windows_stat* st);

View file

@ -67,6 +67,17 @@ static int clean_dir(struct snapraid_state* state, const char* dir)
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &st);
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows */
if (st.st_mode == 0) {
if (lstat(path_next, &st) != 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#else
/* get lstat info about the file */
if (lstat(path_next, &st) != 0) {

View file

@ -212,27 +212,32 @@ static inline int hardlink(const char* a, const char* b)
int devuuid(uint64_t device, char* uuid, size_t size);
/**
* Special value returned when the file doesn't have a real offset.
* For example, because it's stored in the NTFS MFT.
* Physical offset not yet read.
*/
#define FILEPHY_WITHOUT_OFFSET 0
#define FILEPHY_UNREAD_OFFSET 0
/**
* Special value returned when the filesystem doesn't report any offset for unknown reason.
*/
#define FILEPHY_UNREPORTED_OFFSET 1
/**
* Special value returned when the file doesn't have a real offset.
* For example, because it's stored in the NTFS MFT.
*/
#define FILEPHY_WITHOUT_OFFSET 2
/**
* Value indicating real offsets. All offsets greater or equal at this one are real.
*/
#define FILEPHY_REAL_OFFSET 2
#define FILEPHY_REAL_OFFSET 3
/**
* Get the physcal address of the specified file.
* This is expected to be just a hint and not necessarely correct or unique.
* Returns 0 on success.
*/
int filephy(const char* path, struct stat* st, uint64_t* physical);
int filephy(const char* path, uint64_t size, uint64_t* physical);
/**
* Checks if the underline filesystem support persistent inodes.
@ -251,7 +256,7 @@ uint64_t tick(void);
/**
* Initializes the system.
*/
void os_init(void);
void os_init(int opt);
/**
* Deintialize the system.

View file

@ -290,6 +290,85 @@ static int file_is_full_hashed_and_stable(struct snapraid_state* state, struct s
return 1;
}
static void scan_file_delayed_insert(struct snapraid_scan* scan, struct snapraid_file* file)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
/* if we sort for physical offsets we have to read them for the new files */
if (state->opt.force_order == SORT_PHYSICAL) {
char path_next[PATH_MAX];
pathprint(path_next, sizeof(path_next), "%s%s", disk->dir, file->sub);
if (filephy(path_next, file->size, &file->physical) != 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error in getting the physical offset of file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
tommy_list_insert_tail(&scan->file_insert_list, &file->nodelist, file);
}
/**
* Refresh the file info.
*
* This is needed by Windows as the normal way to list directories may report not
* updated info. Only the GetFileInformationByHandle() func, called file-by-file,
* relly ensures to return synced info.
*/
static void scan_file_refresh(struct snapraid_scan* scan, const char* sub, struct stat* st)
{
#if HAVE_LSTAT_SYNC
struct snapraid_disk* disk = scan->disk;
/* if the st_sync is not set, ensure to get synced info */
if (st->st_sync == 0) {
char path_next[PATH_MAX];
struct stat synced_st;
pathprint(path_next, sizeof(path_next), "%s%s", disk->dir, sub);
if (lstat_sync(path_next, &synced_st) != 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error in stat file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
if (st->st_mtime != synced_st.st_mtime
|| st->st_mtimensec != synced_st.st_mtimensec
) {
fprintf(stderr, "WARNING! Detected uncached time change for file '%s'\n", sub);
fprintf(stderr, "It's better if you run SnapRAID without other processes running.\n");
st->st_mtime = synced_st.st_mtime;
st->st_mtimensec = synced_st.st_mtimensec;
}
if (st->st_size != synced_st.st_size) {
fprintf(stderr, "WARNING! Detected uncached size change for file '%s'\n", sub);
fprintf(stderr, "It's better if you run SnapRAID without other processes running.\n");
st->st_size = synced_st.st_size;
}
if (st->st_ino != synced_st.st_ino) {
fprintf(stderr, "DANGER! Detected uncached inode change for file '%s'\n", sub);
fprintf(stderr, "Please ensure to run SnapRAID without other processes running.\n");
/* at this point, it's too late to change inode */
/* and having inconsistent inodes may result to internal failures */
/* so, it's better to abort */
exit(EXIT_FAILURE);
}
}
#else
(void)scan;
(void)sub;
(void)st;
#endif
}
/**
* Keeps the file as it's (or with only a name/inode modification).
*
@ -314,7 +393,7 @@ static void scan_file_keep(struct snapraid_scan* scan, struct snapraid_file* fil
tommy_list_remove_existing(&disk->filelist, &file->nodelist);
/* insert the file in the delayed block allocation */
tommy_list_insert_tail(&scan->file_insert_list, &file->nodelist, file);
scan_file_delayed_insert(scan, file);
}
}
@ -442,7 +521,7 @@ static void scan_file_insert(struct snapraid_scan* scan, struct snapraid_file* f
/**
* Processes a file.
*/
static void scan_file(struct snapraid_scan* scan, int output, const char* sub, const struct stat* st, uint64_t physical)
static void scan_file(struct snapraid_scan* scan, int output, const char* sub, struct stat* st, uint64_t physical)
{
struct snapraid_state* state = scan->state;
struct snapraid_disk* disk = scan->disk;
@ -450,6 +529,7 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
int add_insert;
int add_change;
tommy_node* i;
int is_original_file_size_different_than_zero;
/*
* If the disk has persistent inodes and UUID, try a search on the past inodes,
@ -499,16 +579,17 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
) {
/* check if multiple files have the same inode */
if (file_flag_has(file, FILE_IS_PRESENT)) {
if (st->st_nlink > 1) {
/* it's a hardlink */
scan_link(scan, output, sub, file->sub, FILE_IS_HARDLINK);
return;
} else {
#if HAVE_STRUCT_STAT_ST_NLINK
if (st->st_nlink <= 1) {
/* LCOV_EXCL_START */
fprintf(stderr, "Internal inode '%" PRIu64 "' inconsistency for file '%s%s' already present\n", (uint64_t)st->st_ino, disk->dir, sub);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
/* it's a hardlink */
scan_link(scan, output, sub, file->sub, FILE_IS_HARDLINK);
return;
}
/* mark as present */
@ -596,6 +677,7 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
add_insert = 0;
add_change = 0;
is_original_file_size_different_than_zero = 0;
/* then try finding it by name */
file = tommy_hashdyn_search(&disk->pathset, file_path_compare_to_arg, sub, file_path_hash(sub));
@ -697,19 +779,8 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
/* here if the file is changed but with the correct name */
/* do a safety check to ensure that the common ext4 case of zeroing */
/* the size of a file after a crash doesn't propagate to the backup */
if (file->size != 0 && st->st_size == 0) {
if (!state->opt.force_zero) {
/* LCOV_EXCL_START */
fprintf(stderr, "The file '%s%s' has unexpected zero size! If this an expected state\n", disk->dir, sub);
fprintf(stderr, "you can '%s' anyway usinge 'snapraid --force-zero %s'\n", state->command, state->command);
fprintf(stderr, "Instead, it's possible that after a kernel crash this file was lost,\n");
fprintf(stderr, "and you can use 'snapraid --filter %s fix' to recover it.\n", sub);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* keep track if the original file was not of zero size */
is_original_file_size_different_than_zero = file->size != 0;
/* it has the same name, so it's an update */
add_change = 1;
@ -719,10 +790,10 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
printf("update %s%s\n", disk->dir, file->sub);
}
/* remove it */
/* remove it, and continue to insert it again */
scan_file_remove(scan, file);
/* and continue to reinsert it */
/* and continue to reinsert it again */
} else {
/* if the name doesn't exist, it's a new file */
add_insert = 1;
@ -735,6 +806,25 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
/* and continue to insert it */
}
/* refresh the info, to ensure that they are synced, */
/* not that we refresh only the info of the new or modified files */
/* because this is slow operation */
scan_file_refresh(scan, sub, st);
/* do a safety check to ensure that the common ext4 case of zeroing */
/* the size of a file after a crash doesn't propagate to the backup */
if (is_original_file_size_different_than_zero && st->st_size == 0) {
if (!state->opt.force_zero) {
/* LCOV_EXCL_START */
fprintf(stderr, "The file '%s%s' has unexpected zero size! If this an expected state\n", disk->dir, sub);
fprintf(stderr, "you can '%s' anyway usinge 'snapraid --force-zero %s'\n", state->command, state->command);
fprintf(stderr, "Instead, it's possible that after a kernel crash this file was lost,\n");
fprintf(stderr, "and you can use 'snapraid --filter %s fix' to recover it.\n", sub);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
/* insert it */
file = file_alloc(state->block_size, sub, st->st_size, st->st_mtime, STAT_NSEC(st), st->st_ino, physical);
@ -788,7 +878,7 @@ static void scan_file(struct snapraid_scan* scan, int output, const char* sub, c
tommy_hashdyn_insert(&disk->stampset, &file->stampset, file, file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec));
/* insert the file in the delayed block allocation */
tommy_list_insert_tail(&scan->file_insert_list, &file->nodelist, file);
scan_file_delayed_insert(scan, file);
}
/**
@ -1018,6 +1108,8 @@ static int scan_dir(struct snapraid_scan* scan, int output, const char* dir, con
#if HAVE_STRUCT_DIRENT_D_STAT
/* convert dirent to lstat result */
dirent_lstat(dd, &entry->d_stat);
/* note that at this point the st_mode may be 0 */
#endif
memcpy(entry->d_name, dd->d_name, name_len + 1);
@ -1083,6 +1175,20 @@ static int scan_dir(struct snapraid_scan* scan, int output, const char* dir, con
/* get the type from stat */
st = DSTAT(path_next, dd, &st_buf);
#if HAVE_STRUCT_DIRENT_D_STAT
/* if the st_mode field is missing, takes care to fill it using normal lstat() */
/* at now this can happen only in Windows, but we cannot call here lstat_sync(), */
/* because we don't know what kind of file is it, and lstat_sync() doesn't always work */
if (st->st_mode == 0) {
if (lstat(path_next, st) != 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error in stat file/directory '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
#endif
if (S_ISREG(st->st_mode))
type = 0;
else if (S_ISLNK(st->st_mode))
@ -1095,33 +1201,25 @@ static int scan_dir(struct snapraid_scan* scan, int output, const char* dir, con
if (type == 0) { /* REG */
if (filter_path(&state->filterlist, disk->name, sub_next) == 0) {
uint64_t physical;
/* late stat */
if (!st) st = DSTAT(path_next, dd, &st_buf);
/* late stat, if not yet called */
if (!st)
st = DSTAT(path_next, dd, &st_buf);
#if HAVE_LSTAT_EX
/* get inode info about the file, Windows needs an additional step */
/* also for hardlink, the real size of the file is read here */
if (lstat_ex(path_next, st) != 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error in stat_inode file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
#endif
if (state->opt.force_order == SORT_PHYSICAL) {
if (filephy(path_next, st, &physical) != 0) {
#if HAVE_LSTAT_SYNC
/* if the st_ino field is missing, takes care to fill it using the extended lstat() */
/* this can happen only in Windows */
if (st->st_ino == 0) {
if (lstat_sync(path_next, st) != 0) {
/* LCOV_EXCL_START */
fprintf(stderr, "Error in getting the physical offset of file '%s'. %s.\n", path_next, strerror(errno));
fprintf(stderr, "Error in stat file '%s'. %s.\n", path_next, strerror(errno));
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
} else {
physical = 0;
}
#endif
scan_file(scan, output, sub_next, st, physical);
scan_file(scan, output, sub_next, st, FILEPHY_UNREAD_OFFSET);
processed = 1;
} else {
if (state->opt.verbose) {
@ -1161,8 +1259,9 @@ static int scan_dir(struct snapraid_scan* scan, int output, const char* dir, con
} else if (type == 2) { /* DIR */
if (filter_dir(&state->filterlist, disk->name, sub_next) == 0) {
#ifndef _WIN32
/* late stat */
if (!st) st = DSTAT(path_next, dd, &st_buf);
/* late stat, if not yet called */
if (!st)
st = DSTAT(path_next, dd, &st_buf);
/* in Unix don't follow mount points in different devices */
/* in Windows we are already skipping them reporting them as special files */
@ -1191,8 +1290,9 @@ static int scan_dir(struct snapraid_scan* scan, int output, const char* dir, con
}
} else {
if (filter_path(&state->filterlist, disk->name, sub_next) == 0) {
/* late stat */
if (!st) st = DSTAT(path_next, dd, &st_buf);
/* late stat, if not yet called */
if (!st)
st = DSTAT(path_next, dd, &st_buf);
fprintf(stderr, "WARNING! Ignoring special '%s' file '%s'\n", stat_desc(st), path_next);
} else {
@ -1303,6 +1403,9 @@ void state_scan(struct snapraid_state* state, int output)
uint64_t phy_last;
struct snapraid_file* phy_file_last;
if (!output)
printf("Mapping disk %s...\n", disk->name);
/* check for removed files */
node = disk->filelist;
while (node) {
@ -1379,20 +1482,24 @@ void state_scan(struct snapraid_state* state, int output)
/* insert all the new files, we insert them only after the deletion */
/* to reuse the just freed space */
/* also check if the physical offset reported are fakes or not */
node = scan->file_insert_list;
phy_count = 0;
phy_dup = 0;
phy_last = -1;
phy_last = FILEPHY_UNREAD_OFFSET;
phy_file_last = 0;
while (node) {
struct snapraid_file* file = node->data;
/* if the file is not empty, count duplicate physical offsets */
if (state->opt.force_order == SORT_PHYSICAL && file->size != 0) {
if (phy_file_last != 0 && file->physical == phy_last && phy_last != FILEPHY_WITHOUT_OFFSET) {
/* if verbose, prints the list of duplicates */
/* if they are supposed real offsets */
if (state->opt.verbose && phy_last != FILEPHY_UNREPORTED_OFFSET) {
if (phy_file_last != 0 && file->physical == phy_last
/* files without offset are expected to have duplicates */
&& phy_last != FILEPHY_WITHOUT_OFFSET
) {
/* if verbose, prints the list of duplicates real offsets */
/* other cases are for offsets not supported, so we don't need to report them file by file */
if (state->opt.verbose && phy_last >= FILEPHY_REAL_OFFSET) {
fprintf(stderr, "WARNING! Files '%s%s' and '%s%s' have the same physical offset %" PRId64 ".\n", disk->dir, phy_file_last->sub, disk->dir, file->sub, phy_last);
}
++phy_dup;

View file

@ -233,6 +233,7 @@ void config(char* conf, size_t conf_size, const char* argv0)
#define OPT_TEST_SKIP_PARITY_ACCESS 276
#define OPT_TEST_EXPECT_FAILURE 277
#define OPT_TEST_RUN 278
#define OPT_TEST_FORCE_SCAN_WINFIND 279
#if HAVE_GETOPT_LONG
struct option long_options[] = {
@ -328,6 +329,9 @@ struct option long_options[] = {
/* Run some command after loading the state and before the command */
{ "test-run", 1, 0, OPT_TEST_RUN },
/* Use the FindFirst/Next approach in Windows to list files */
{ "test-force-scan-winfind", 0, 0, OPT_TEST_FORCE_SCAN_WINFIND },
{ 0, 0, 0, 0 }
};
#endif
@ -384,15 +388,12 @@ int main(int argc, char* argv[])
int lock;
const char* gen_conf;
const char* run;
int speedtest;
int period;
time_t t;
struct tm* tm;
int i;
os_init();
raid_init();
crc32c_init();
/* always different random numbers */
srand(time(0));
@ -412,6 +413,7 @@ int main(int argc, char* argv[])
log = 0;
lock = 0;
gen_conf = 0;
speedtest = 0;
run = 0;
opterr = 0;
@ -546,8 +548,8 @@ int main(int argc, char* argv[])
version();
exit(EXIT_SUCCESS);
case 'T' :
speed(period);
exit(EXIT_SUCCESS);
speedtest = 1;
break;
case 'C' :
gen_conf = optarg;
break;
@ -623,6 +625,9 @@ int main(int argc, char* argv[])
case OPT_TEST_RUN :
run = optarg;
break;
case OPT_TEST_FORCE_SCAN_WINFIND :
opt.force_scan_winfind = 1;
break;
default :
/* LCOV_EXCL_START */
fprintf(stderr, "Unknown option '%c'\n", (char)c);
@ -631,6 +636,16 @@ int main(int argc, char* argv[])
}
}
os_init(opt.force_scan_winfind);
raid_init();
crc32c_init();
if (speedtest != 0) {
speed(period);
os_done();
exit(EXIT_SUCCESS);
}
if (gen_conf != 0) {
generate_configuration(gen_conf);
os_done();

View file

@ -367,7 +367,7 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
/* copy the options */
state->opt = *opt;
/* if unsed, sort by physical order */
/* if unset, sort by physical order */
if (!state->opt.force_order)
state->opt.force_order = SORT_PHYSICAL;

View file

@ -90,6 +90,7 @@ struct snapraid_option {
int force_scrub_even; /**< Force scrub of all the even blocks. */
int force_content_write; /**< Force the update of the content file. */
int force_content_text; /**< Force the use of text version of content file. */
int force_scan_winfind; /**< Force the use of FindFirst/Next in Windows to list directories. */
};
/**

View file

@ -129,7 +129,7 @@ int devuuid(uint64_t device, char* uuid, size_t uuid_size)
}
#endif
int filephy(const char* path, struct stat* st, uint64_t* physical)
int filephy(const char* path, uint64_t size, uint64_t* physical)
{
#if HAVE_LINUX_FIEMAP_H
/* In Linux gets the real physical address of the file */
@ -162,7 +162,7 @@ int filephy(const char* path, struct stat* st, uint64_t* physical)
}
/* if the file is empty, FIBMAP doesn't work, and we don't even try to use it */
if (st->st_size == 0) {
if (size == 0) {
*physical = FILEPHY_WITHOUT_OFFSET;
if (close(f) == -1)
return -1;
@ -199,7 +199,7 @@ int filephy(const char* path, struct stat* st, uint64_t* physical)
*physical = FILEPHY_UNREPORTED_OFFSET;
(void)path; /* not used here */
(void)st;
(void)size;
#endif
return 0;
@ -267,8 +267,9 @@ uint64_t tick(void)
#endif
}
void os_init(void)
void os_init(int opt)
{
(void)opt;
}
void os_done(void)

View file

@ -42,7 +42,7 @@ AC_TYPE_UINT8_T
AC_TYPE_INT8_T
AC_STRUCT_DIRENT_D_INO
AC_STRUCT_DIRENT_D_TYPE
AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec, struct stat.st_mtimespec.tv_nsec], [], [], [[
AC_CHECK_MEMBERS([struct stat.st_nlink, struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec, struct stat.st_mtimespec.tv_nsec], [], [], [[
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif