mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Merge branch 'dirstream'
This commit is contained in:
commit
84552244ba
16 changed files with 607 additions and 244 deletions
1
HISTORY
1
HISTORY
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
2
TODO
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
495
cmdline/mingw.c
495
cmdline/mingw.c
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
209
cmdline/scan.c
209
cmdline/scan.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue