From: Eric Bollengier Date: Fri, 28 Jan 2011 09:50:31 +0000 (+0100) Subject: Detect mount/junction points and ignore junctions in Windows X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=2873e2a9dc34e5f95b7882bff85829b902c827d0;p=bacula%2Fbacula Detect mount/junction points and ignore junctions in Windows --- diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index 72bef802be..78ec13f01e 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -353,13 +353,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) case FT_NOFSCHG: /* Suppress message for /dev filesystems */ if (!is_in_fileset(ff_pkt)) { -#ifdef HAVE_WIN32 - Jmsg(jcr, M_INFO, 1, _(" %s is a junction point or a different filesystem. Will not descend from %s into it.\n"), - ff_pkt->fname, ff_pkt->top_fname); -#else Jmsg(jcr, M_INFO, 1, _(" %s is a different filesystem. Will not descend from %s into it.\n"), ff_pkt->fname, ff_pkt->top_fname); -#endif } ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; @@ -373,6 +368,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) ff_pkt->fname); break; case FT_REPARSE: + case FT_JUNCTION: case FT_DIREND: Dmsg1(130, "FT_DIREND: %s\n", ff_pkt->link); break; @@ -538,7 +534,7 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) do_read = ff_pkt->statp.st_size > 0; #endif } else if (ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO || - ff_pkt->type == FT_REPARSE || + ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION || (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) { do_read = true; } @@ -557,7 +553,8 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) tid = NULL; } int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0; - ff_pkt->bfd.reparse_point = ff_pkt->type == FT_REPARSE; + ff_pkt->bfd.reparse_point = (ff_pkt->type == FT_REPARSE || + ff_pkt->type == FT_JUNCTION); if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0) < 0) { ff_pkt->ff_errno = errno; berrno be; @@ -1197,6 +1194,7 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) break; case FT_DIREND: case FT_REPARSE: + case FT_JUNCTION: /* Here link is the canonical filename (i.e. with trailing slash) */ stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, @@ -1385,6 +1383,7 @@ static void close_vss_backup_session(JCR *jcr) ff_pkt->object_name = (char *)"job_metadata.xml"; ff_pkt->object = (char *)metadata; ff_pkt->object_len = (wcslen(metadata) + 1) * sizeof(WCHAR); + ff_pkt->object_index = (int)time(NULL); save_file(jcr, ff_pkt, true); } } diff --git a/bacula/src/filed/estimate.c b/bacula/src/filed/estimate.c index 3f6f7dc627..7888bc9ffe 100644 --- a/bacula/src/filed/estimate.c +++ b/bacula/src/filed/estimate.c @@ -82,6 +82,7 @@ static int tally_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) case FT_INVALIDFS: case FT_INVALIDDT: case FT_REPARSE: + case FT_JUNCTION: case FT_DIREND: case FT_SPEC: case FT_RAW: diff --git a/bacula/src/filed/verify.c b/bacula/src/filed/verify.c index 5f3841d4c5..287f0a4ad3 100644 --- a/bacula/src/filed/verify.c +++ b/bacula/src/filed/verify.c @@ -108,6 +108,7 @@ static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) jcr->num_files_examined--; /* correct file count */ return 1; /* ignored */ case FT_REPARSE: + case FT_JUNCTION: case FT_DIREND: Dmsg1(30, "FT_DIR saving: %s\n", ff_pkt->fname); break; @@ -196,7 +197,8 @@ static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles, STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, 0, attribs, 0, ff_pkt->link, 0); - } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE) { + } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || + ff_pkt->type == FT_JUNCTION) { /* Here link is the canonical filename (i.e. with trailing slash) */ stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles, STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link, diff --git a/bacula/src/findlib/create_file.c b/bacula/src/findlib/create_file.c index 6a4fddd568..ab8afd2ff5 100644 --- a/bacula/src/findlib/create_file.c +++ b/bacula/src/findlib/create_file.c @@ -362,6 +362,7 @@ int create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) } /* End inner switch */ case FT_REPARSE: + case FT_JUNCTION: bfd->reparse_point = true; /* Fall through wanted */ case FT_DIRBEGIN: diff --git a/bacula/src/findlib/find.c b/bacula/src/findlib/find.c index 3c60f95219..77ac196dab 100644 --- a/bacula/src/findlib/find.c +++ b/bacula/src/findlib/find.c @@ -445,7 +445,6 @@ static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level) case FT_INVALIDFS: case FT_INVALIDDT: case FT_NOOPEN: - case FT_REPARSE: // return ff->file_save(jcr, ff, top_level); /* These items can be filtered */ @@ -459,6 +458,8 @@ static int our_callback(JCR *jcr, FF_PKT *ff, bool top_level) case FT_FIFO: case FT_SPEC: case FT_DIRNOCHG: + case FT_REPARSE: + case FT_JUNCTION: if (accept_file(ff)) { return ff->file_save(jcr, ff, top_level); } else { diff --git a/bacula/src/findlib/find_one.c b/bacula/src/findlib/find_one.c index 6cad341518..64a5ec0dd8 100644 --- a/bacula/src/findlib/find_one.c +++ b/bacula/src/findlib/find_one.c @@ -586,8 +586,27 @@ find_one_file(JCR *jcr, FF_PKT *ff_pkt, * if st_rdev is 2, it is a mount point */ #if defined(HAVE_WIN32) + /* + * A reparse point (WIN32_REPARSE_POINT) + * is something special like one of the following: + * IO_REPARSE_TAG_DFS 0x8000000A + * IO_REPARSE_TAG_DFSR 0x80000012 + * IO_REPARSE_TAG_HSM 0xC0000004 + * IO_REPARSE_TAG_HSM2 0x80000006 + * IO_REPARSE_TAG_SIS 0x80000007 + * IO_REPARSE_TAG_SYMLINK 0xA000000C + * + * A junction point is a: + * IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 + * which can be either a link to a Volume (WIN32_MOUNT_POINT) + * or a link to a directory (WIN32_JUNCTION_POINT) + * + * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT + */ if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) { ff_pkt->type = FT_REPARSE; + } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT) { + ff_pkt->type = FT_JUNCTION; } #endif /* @@ -599,7 +618,8 @@ find_one_file(JCR *jcr, FF_PKT *ff_pkt, * in the directory is seen (i.e. the FT_DIREND). */ rtn_stat = handle_file(jcr, ff_pkt, top_level); - if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE) { /* ignore or error status */ + if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE || + ff_pkt->type == FT_JUNCTION) { /* ignore or error status */ free(link); return rtn_stat; } diff --git a/bacula/src/win32/compat/compat.cpp b/bacula/src/win32/compat/compat.cpp index d662f0279f..cb83a9b36d 100644 --- a/bacula/src/win32/compat/compat.cpp +++ b/bacula/src/win32/compat/compat.cpp @@ -41,6 +41,17 @@ #include "jcr.h" #include "findlib/find.h" +/* Note, if you want to see what Windows variables and structures + * are defined, bacula.h includes , which is found in: + * + * cross-tools/mingw32/mingw32/include + * or + * cross-tools/mingw-w64/x86_64-pc-mingw32/include + * + * depending on whether we are building the 32 bit version or + * the 64 bit version. + */ + static const int dbglvl = 500; #define b_errno_win32 (1<<29) @@ -404,6 +415,30 @@ make_wchar_win32_path(POOLMEM *pszUCSPath, BOOL *pBIsRawPath /*= NULL*/) return (POOLMEM *)pwszBuf; } +/* + * Convert from WCHAR (UCS) to UTF-8 + */ +int +wchar_2_UTF8(POOLMEM **pszUTF, const wchar_t *pszUCS) +{ + /** + * The return value is the number of bytes written to the buffer. + * The number includes the byte for the null terminator. + */ + + if (p_WideCharToMultiByte) { + int nRet = p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,NULL,0,NULL,NULL); + *pszUTF = check_pool_memory_size(*pszUTF, nRet); + return p_WideCharToMultiByte(CP_UTF8,0,pszUCS,-1,*pszUTF,nRet,NULL,NULL); + + } else { + return 0; + } +} + +/* + * Convert from WCHAR (UCS) to UTF-8 + */ int wchar_2_UTF8(char *pszUTF, const wchar_t *pszUCS, int cchChar) { @@ -638,6 +673,11 @@ static const char *errorString(void) } +/* + * This is only called for directories, and is used to get the directory + * attributes and find out if we have a junction point or a mount point + * or other kind of "funny" directory. + */ static int statDir(const char *file, struct stat *sb) { @@ -652,6 +692,7 @@ statDir(const char *file, struct stat *sb) FILETIME *pftLastAccessTime; FILETIME *pftLastWriteTime; FILETIME *pftCreationTime; + HANDLE h = INVALID_HANDLE_VALUE; /* * Oh, cool, another exception: Microsoft doesn't let us do @@ -669,7 +710,6 @@ statDir(const char *file, struct stat *sb) return 0; } - HANDLE h = INVALID_HANDLE_VALUE; // use unicode if (p_FindFirstFileW) { @@ -737,10 +777,62 @@ statDir(const char *file, struct stat *sb) * filesystem). */ if (*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) { - sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */ + sb->st_rdev = WIN32_MOUNT_POINT; + } else { + sb->st_rdev = 0; + } + if ((*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT)) { + sb->st_rdev = WIN32_MOUNT_POINT; /* mount point */ + /* + * Now to find out if the directory is a mount point or + * a reparse point, we must do a song and a dance. + * Explicitly open the file to read the reparse point, then + * call DeviceIoControl to find out if it points to a Volume + * or to a directory. + */ + h = INVALID_HANDLE_VALUE; + if (p_GetFileAttributesW) { + POOLMEM* pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, file); + if (p_CreateFileW) { + h = CreateFileW((LPCWSTR)pwszBuf, GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + } + free_pool_memory(pwszBuf); + } else if (p_GetFileAttributesA) { + h = CreateFileA(file, GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + } + if (h != INVALID_HANDLE_VALUE) { + char dummy[1000]; + REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER *)dummy; + rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + DWORD bytes; + bool ok; + ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, + NULL, 0, /* in buffer, bytes */ + (LPVOID)rdb, (DWORD)sizeof(dummy), /* out buffer, btyes */ + (LPDWORD)&bytes, (LPOVERLAPPED)0); + if (ok) { + POOLMEM *utf8 = get_pool_memory(PM_NAME); + wchar_2_UTF8(&utf8, (wchar_t *)rdb->SymbolicLinkReparseBuffer.PathBuffer); + Dmsg2(dbglvl, "Junction %s points to: %s\n", file, utf8); + if (strncasecmp(utf8, "\\??\\volume{", 11) == 0) { + sb->st_rdev = WIN32_MOUNT_POINT; + } else { + /* It points to a directory so we ignore it. */ + sb->st_rdev = WIN32_JUNCTION_POINT; + } + free_pool_memory(utf8); + } + CloseHandle(h); } else { - sb->st_rdev = WIN32_REPARSE_POINT; /* reparse point */ + Dmsg1(dbglvl, "Invalid handle from CreateFile(%s)\n", file); } } Dmsg2(dbglvl, "st_rdev=%d file=%s\n", sb->st_rdev, file); @@ -1297,9 +1389,10 @@ readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) // copy unicode if (dp->valid_w) { - char szBuf[MAX_PATH_UTF8+1]; - wchar_2_UTF8(szBuf,dp->data_w.cFileName); + POOLMEM *szBuf = get_pool_memory(PM_NAME); + wchar_2_UTF8(&szBuf, dp->data_w.cFileName); dp->offset += copyin(*entry, szBuf); + free_pool_memory(szBuf); } else if (dp->valid_a) { // copy ansi (only 1 will be valid) dp->offset += copyin(*entry, dp->data_a.cFileName); } @@ -1543,7 +1636,7 @@ win32_getcwd(char *buf, int maxlen) } else if (p_GetCurrentDirectoryA) n = p_GetCurrentDirectoryA(maxlen, buf); - if (n == 0 || n > maxlen) return NULL; + if (n <= 0 || n > maxlen) return NULL; if (n+1 > maxlen) return NULL; if (n != 3) { @@ -2151,10 +2244,11 @@ typedef struct s_bpipe { */ static void -CloseIfValid(HANDLE handle) +CloseHandleIfValid(HANDLE handle) { - if (handle != INVALID_HANDLE_VALUE) + if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); + } } BPIPE * @@ -2272,12 +2366,12 @@ open_bpipe(char *prog, int wait, const char *mode) cleanup: - CloseIfValid(hChildStdoutRd); - CloseIfValid(hChildStdoutRdDup); - CloseIfValid(hChildStdinWr); - CloseIfValid(hChildStdinWrDup); + CloseHandleIfValid(hChildStdoutRd); + CloseHandleIfValid(hChildStdoutRdDup); + CloseHandleIfValid(hChildStdinWr); + CloseHandleIfValid(hChildStdinWrDup); - free((void *) bpipe); + free((void *)bpipe); errno = b_errno_win32; /* do GetLastError() for error code */ return NULL; } diff --git a/bacula/src/win32/compat/compat.h b/bacula/src/win32/compat/compat.h index 13e01ba876..c11c3b06e5 100644 --- a/bacula/src/win32/compat/compat.h +++ b/bacula/src/win32/compat/compat.h @@ -1,7 +1,7 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2004-2009 Free Software Foundation Europe e.V. + Copyright (C) 2004-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. @@ -34,10 +34,6 @@ /* * Author : Christopher S. Hull * Created On : Fri Jan 30 13:00:51 2004 - * Last Modified By: Thorsten Engel - * Last Modified On: Fri Apr 22 19:30:00 2004 - * Update Count : 218 - * $Id$ */ @@ -60,8 +56,37 @@ #ifdef MINGW64 #include #define _declspec __declspec + +/* Missing in 64 bit mingw */ +typedef struct _REPARSE_DATA_BUFFER { + DWORD ReparseTag; + WORD ReparseDataLength; + WORD Reserved; + union { + struct { + WORD SubstituteNameOffset; + WORD SubstituteNameLength; + WORD PrintNameOffset; + WORD PrintNameLength; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + WORD SubstituteNameOffset; + WORD SubstituteNameLength; + WORD PrintNameOffset; + WORD PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + BYTE DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; + #endif +#include + #ifdef _WIN64 # define GWL_USERDATA GWLP_USERDATA #endif @@ -381,8 +406,6 @@ void closelog(); void openlog(const char *ident, int option, int facility); #endif //HAVE_MINGW -void LogErrorMsg(const char *message); - /* Don't let OS go to sleep (usually a Laptop) while we are backing up */ void prevent_os_suspensions(); void allow_os_suspensions(); @@ -390,7 +413,11 @@ void allow_os_suspensions(); typedef DWORD EXECUTION_STATE; #ifndef ES_CONTINUOUS #define ES_CONTINUOUS 0x80000000 +#endif +#ifndef ES_SYSTEM_REQUIRED #define ES_SYSTEM_REQUIRED 0x00000001 +#endif +#ifndef ES_DISPLAY_REQUIRED #define ES_DISPLAY_REQUIRED 0x00000002 #endif #ifndef ES_USER_PRESENT @@ -400,6 +427,8 @@ typedef DWORD EXECUTION_STATE; WINBASEAPI EXECUTION_STATE WINAPI SetThreadExecutionState(EXECUTION_STATE esFlags); +extern void LogErrorMsg(const char *message); + #if !defined(INVALID_FILE_ATTRIBUTES) #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif diff --git a/bacula/src/win32/compat/winconfig.h b/bacula/src/win32/compat/winconfig.h index be02604bf1..567faa2826 100644 --- a/bacula/src/win32/compat/winconfig.h +++ b/bacula/src/win32/compat/winconfig.h @@ -632,4 +632,7 @@ /* Use long unsigned int for ioctl request */ #define HAVE_IOCTL_ULINT_REQUEST +/* Little Endian */ +#define HAVE_LITTLE_ENDIAN 1 + #endif /* __WINCONFIG_H */