From 8184f63c6d8559edf5aff0b2bfe6a83e6097d76e Mon Sep 17 00:00:00 2001 From: Oleg Livshyts Date: Wed, 3 Dec 2014 10:32:49 +0100 Subject: [PATCH] We now support symlink-like objects on windows Support was added for the following types of symlinks: - Symlinks on Files - Symlinks on Directories - Junction Points These are implemented in windows as Reparse Points. All these objects are treated like ordinary symlinks (FT_LNK) in bareos, also Junction Points and Symlinks on Directories which are directories in windows logic. The Information on what kind of symlink-like object we have is stored in the st_rdev member of the stat information. There we have now - FILE_ATTRIBUTES_JUNCTION_POINT - FILE_ATTRIBUTES_SYMBOLIC_LINK During Backup, the function "get_symlink_data" gets the information where the symlink/junction/volume mount points points to. During restore, the function "symlink" creates the symlink-like object. To be able to determine what kind of "symlink" needs to be created, it also gets the st_rdev information. Depending on what kind of symlink it needs to create it either calls the windows api function "CreateSymbolicLink" or the self-implemented Function "CreateJunction" The symlink target is not altered during restore, even if it is restored with a prefix (where), because this makes absolute links unusable. Fixes #314: On Windows 2008 and Windows 2012 the junction points / links are not backed up properly Signed-off-by: Philipp Storz Signed-off-by: Marco van Wieringen --- src/filed/backup.c | 11 +- src/findlib/create_file.c | 50 ++++- src/findlib/find_one.c | 8 +- src/findlib/mkpath.c | 51 +++-- src/lib/attr.c | 6 - src/win32/compat/compat.c | 336 +++++++++++++++++++++++++++--- src/win32/compat/include/compat.h | 16 +- 7 files changed, 403 insertions(+), 75 deletions(-) diff --git a/src/filed/backup.c b/src/filed/backup.c index 8fb01173cc5..4be3916cdea 100644 --- a/src/filed/backup.c +++ b/src/filed/backup.c @@ -732,9 +732,12 @@ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) #else 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_JUNCTION || - (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) { + } else if (ff_pkt->type == FT_RAW || + ff_pkt->type == FT_FIFO || + ff_pkt->type == FT_REPARSE || + ff_pkt->type == FT_JUNCTION || + (!is_portable_backup(&ff_pkt->bfd) && + ff_pkt->type == FT_DIREND)) { do_read = true; } @@ -1357,6 +1360,7 @@ bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) strip_path(ff_pkt); } switch (ff_pkt->type) { + case FT_JUNCTION: case FT_LNK: case FT_LNKSAVED: Dmsg3(300, "Link %d %s to %s\n", jcr->JobFiles, ff_pkt->fname, ff_pkt->link); @@ -1366,7 +1370,6 @@ 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) */ status = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, ff_pkt->type, ff_pkt->link, 0, attribs.c_str(), 0, 0, diff --git a/src/findlib/create_file.c b/src/findlib/create_file.c index 00ba64b473e..1da1f1f0ae1 100644 --- a/src/findlib/create_file.c +++ b/src/findlib/create_file.c @@ -41,7 +41,6 @@ static int separate_path_and_file(JCR *jcr, char *fname, char *ofile); static int path_already_seen(JCR *jcr, char *path, int pnl); - /* * Create the file, or the directory * @@ -306,15 +305,6 @@ int create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) Dmsg1(400, "FT_SPEC %s\n", attr->ofname); return CF_CREATED; - case FT_LNK: - Dmsg2(130, "FT_LNK should restore: %s -> %s\n", attr->ofname, attr->olname); - if (symlink(attr->olname, attr->ofname) != 0 && errno != EEXIST) { - berrno be; - Qmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"), - attr->ofname, attr->olname, be.bstrerror()); - return CF_ERROR; - } - return CF_CREATED; case FT_LNKSAVED: /* Hard linked, file already saved */ Dmsg2(130, "Hard link %s => %s\n", attr->ofname, attr->olname); @@ -366,6 +356,45 @@ int create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) } return CF_CREATED; + +#endif /* HAVE_WIN32 */ +#ifdef HAVE_WIN32 + case FT_LNK: + /* + * Handle Windows Symlink-Like Reparse Points + * - Directory Symlinks + * - File Symlinks + * - Volume Mount Points + * - Junctions + */ + Dmsg2(130, "FT_LNK should restore: %s -> %s\n", attr->ofname, attr->olname); + if (attr->statp.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT) { + /* + * We do not restore volume mount points + */ + Dmsg0(130, "Skipping Volume Mount Point\n"); + return CF_SKIP; + } + if (win32_symlink(attr->olname, attr->ofname, attr->statp.st_rdev) != 0 && errno != EEXIST) { + berrno be; + Qmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"), + attr->ofname, attr->olname, be.bstrerror()); + return CF_ERROR; + } + return CF_CREATED; +#else + case FT_LNK: + /* + * Unix/Linux symlink handling + */ + Dmsg2(130, "FT_LNK should restore: %s -> %s\n", attr->ofname, attr->olname); + if (symlink(attr->olname, attr->ofname) != 0 && errno != EEXIST) { + berrno be; + Qmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"), + attr->ofname, attr->olname, be.bstrerror()); + return CF_ERROR; + } + return CF_CREATED; #endif } /* End inner switch */ @@ -381,7 +410,6 @@ int create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) if (!makepath(attr, attr->ofname, new_mode, parent_mode, uid, gid, 0)) { return CF_ERROR; } - /* * If we are using the Win32 Backup API, we open the directory so * that the security info will be read and saved. diff --git a/src/findlib/find_one.c b/src/findlib/find_one.c index 9bd10a8e507..b1408a3fa7d 100644 --- a/src/findlib/find_one.c +++ b/src/findlib/find_one.c @@ -590,9 +590,7 @@ static inline int process_directory(JCR *jcr, FF_PKT *ff_pkt, } #if defined(HAVE_WIN32) - if (ff_pkt->statp.st_rdev & FILE_ATTRIBUTES_JUNCTION_POINT) { - ff_pkt->type = FT_JUNCTION; - } else if (ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_REPARSE_POINT) { + if (ff_pkt->statp.st_rdev & FILE_ATTRIBUTE_REPARSE_POINT) { ff_pkt->type = FT_REPARSE; } #endif @@ -607,9 +605,9 @@ static inline int process_directory(JCR *jcr, FF_PKT *ff_pkt, */ rtn_stat = handle_file(jcr, ff_pkt, top_level); if (rtn_stat < 1 || - ff_pkt->type == FT_REPARSE || - ff_pkt->type == FT_JUNCTION) { /* ignore or error status */ + ff_pkt->type == FT_REPARSE) { /* ignore or error status */ free(link); + return rtn_stat; } diff --git a/src/findlib/mkpath.c b/src/findlib/mkpath.c index e73ffb90302..42ff833ef0d 100644 --- a/src/findlib/mkpath.c +++ b/src/findlib/mkpath.c @@ -183,12 +183,8 @@ static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t /* * mode is the mode bits to use in creating a new directory - * - * parent_mode are the parent's modes if we need to create parent - * directories. - * + * parent_mode are the parent's modes if we need to create parent directories. * owner and group are to set on any created dirs - * * keep_dir_modes if set means don't change mode bits if dir exists */ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, @@ -227,17 +223,19 @@ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, strip_trailing_slashes(path); /* * Now for one of the complexities. If we are not running as root, - * then if the parent_mode does not have wx user perms, or we are - * setting the userid or group, and the parent_mode has setuid, setgid, - * or sticky bits, we must create the dir with open permissions, then - * go back and patch all the dirs up with the correct perms. + * then if the parent_mode does not have wx user perms, or we are + * setting the userid or group, and the parent_mode has setuid, setgid, + * or sticky bits, we must create the dir with open permissions, then + * go back and patch all the dirs up with the correct perms. * Solution, set everything to 0777, then go back and reset them at the - * end. + * end. */ tmode = 0777; #if defined(HAVE_WIN32) - /* Validate drive letter */ + /* + * Validate drive letter + */ if (path[1] == ':') { char drive[4] = "X:\\"; @@ -263,7 +261,9 @@ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, p = path; #endif - /* Skip leading slash(es) */ + /* + * Skip leading slash(es) + */ while (IsPathSeparator(*p)) { p++; } @@ -282,10 +282,15 @@ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, p++; } } - /* Create final component */ - if (!makedir(jcr, path, tmode, &created)) { - goto bail_out; + /* + * Create final component if not a junction/symlink + */ + if(attr->type != FT_JUNCTION ){ + if (!makedir(jcr, path, tmode, &created)) { + goto bail_out; + } } + if (ndir < max_dirs) { new_dir[ndir++] = created; } @@ -293,10 +298,14 @@ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, Jmsg0(jcr, M_WARNING, 0, _("Too many subdirectories. Some permissions not reset.\n")); } - /* Now set the proper owner and modes */ + /* + * Now set the proper owner and modes + */ #if defined(HAVE_WIN32) - /* Don't propagate the hidden attribute to parent directories */ + /* + * Don't propagate the hidden attribute to parent directories + */ parent_mode &= ~S_ISVTX; if (path[1] == ':') { @@ -307,7 +316,9 @@ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, #else p = path; #endif - /* Skip leading slash(es) */ + /* + * Skip leading slash(es) + */ while (IsPathSeparator(*p)) { p++; } @@ -324,7 +335,9 @@ bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode, } } - /* Set for final component */ + /* + * Set for final component + */ if (i < ndir && new_dir[i++]) { set_own_mod(attr, path, owner, group, mode); } diff --git a/src/lib/attr.c b/src/lib/attr.c index 5e0fc5c2060..2cca7d5aa85 100644 --- a/src/lib/attr.c +++ b/src/lib/attr.c @@ -219,12 +219,6 @@ void build_attr_output_fnames(JCR *jcr, ATTR *attr) attr->olname[0] = 0; add_link = false; } - -#if defined(HAVE_WIN32) - if (attr->lname[1] == ':') { - attr->lname[1] = '/'; /* turn : into / */ - } -#endif fn = attr->lname; /* take whole name */ /* Ensure where is terminated with a slash */ if (add_link && diff --git a/src/win32/compat/compat.c b/src/win32/compat/compat.c index 8f182f83798..3c45db9b79d 100644 --- a/src/win32/compat/compat.c +++ b/src/win32/compat/compat.c @@ -37,10 +37,10 @@ /* * Sanity check to make sure FILE_ATTRIBUTE_VALID_FLAGS is always smaller - * then our define of FILE_ATTRIBUTE_VOLUME_MOUNT_POINT. + * than our define of FILE_ATTRIBUTE_VOLUME_MOUNT_POINT. */ #if FILE_ATTRIBUTE_VOLUME_MOUNT_POINT < FILE_ATTRIBUTE_VALID_FLAGS -#error "FILE_ATTRIBUTE_VALID_FLAGS smaller then FILE_ATTRIBUTE_VALID_FLAGS" +#error "FILE_ATTRIBUTE_VALID_FLAGS smaller than FILE_ATTRIBUTE_VALID_FLAGS" #endif /* @@ -824,6 +824,84 @@ static time_t cvt_ftime_to_utime(const LARGE_INTEGER &time) return (time_t) (mstime & 0xffffffff); } +static inline bool CreateJunction(LPCSTR szJunction, LPCSTR szPath) +{ + DWORD dwRet; + HANDLE hDir; + POOLMEM *buf; + bool retval = false; + int buf_length, path_length, data_length; + REPARSE_DATA_BUFFER *rdb; + char szDestDir[MAX_PATH]; + + /* + * Create a buffer big enough to hold all data. + */ + buf_length = sizeof(REPARSE_DATA_BUFFER) + MAX_PATH * sizeof(WCHAR); + buf = get_pool_memory(PM_NAME); + buf = check_pool_memory_size(buf, buf_length); + rdb = (REPARSE_DATA_BUFFER *)buf; + + bstrncpy(szDestDir, "\\??\\", sizeof(szDestDir)); + bstrncat(szDestDir, szPath, sizeof(szDestDir)); + bstrncat(szDestDir, "\\", sizeof(szDestDir)); + + if (!CreateDirectory(szJunction, NULL)) { + Dmsg1(dbglvl, "CreateDirectory Failed:%s\n", errorString()); + goto bail_out; + } + + hDir = CreateFile(szJunction, + GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + if (hDir == INVALID_HANDLE_VALUE){ + Dmsg0(dbglvl, "INVALID_FILE_HANDLE"); + RemoveDirectory(szJunction); + goto bail_out; + } + + memset(buf, 0, buf_length); + path_length = MultiByteToWideChar(CP_ACP, + 0, + szDestDir, + -1, + rdb->MountPointReparseBuffer.PathBuffer, + MAX_PATH * sizeof(WCHAR)); + + rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + rdb->ReparseDataLength = (path_length + 2) * sizeof(WCHAR) + 6; + rdb->MountPointReparseBuffer.SubstituteNameLength = (path_length - 1) * sizeof(WCHAR); + rdb->MountPointReparseBuffer.PrintNameOffset = path_length * sizeof(WCHAR); + data_length = rdb->ReparseDataLength + 8; + + if (!DeviceIoControl(hDir, + FSCTL_SET_REPARSE_POINT, + (LPVOID)buf, + data_length, + NULL, + 0, + &dwRet, + 0)) { + Dmsg1(dbglvl, "DeviceIoControl Failed:%s\n", errorString()); + CloseHandle(hDir); + RemoveDirectory(szJunction); + goto bail_out; + } + + CloseHandle(hDir); + + retval = true; +bail_out: + + free_pool_memory(buf); + + return retval; +} static const char *errorString(void) { @@ -910,25 +988,36 @@ static inline bool get_volume_mount_point_data(const char *filename, POOLMEM **d } if (h != INVALID_HANDLE_VALUE) { - char dummy[1000]; bool ok; + POOLMEM *buf; + int buf_length; + REPARSE_DATA_BUFFER *rdb; DWORD bytes; - REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER *)dummy; + /* + * Create a buffer big enough to hold all data. + */ + buf_length = sizeof(REPARSE_DATA_BUFFER) + MAX_PATH * sizeof(WCHAR); + buf = get_pool_memory(PM_NAME); + buf = check_pool_memory_size(buf, buf_length); + rdb = (REPARSE_DATA_BUFFER *)buf; + + memset(buf, 0, buf_length); rdb->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; ok = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, /* in buffer, bytes */ - (LPVOID)rdb, - (DWORD)sizeof(dummy), /* out buffer, btyes */ + (LPVOID)buf, + (DWORD)buf_length, /* out buffer, btyes */ (LPDWORD)&bytes, (LPOVERLAPPED)0); if (ok) { - wchar_2_UTF8(devicename, (wchar_t *)rdb->SymbolicLinkReparseBuffer.PathBuffer); + wchar_2_UTF8(devicename, (wchar_t *)rdb->MountPointReparseBuffer.PathBuffer); } CloseHandle(h); + free_pool_memory(buf); } else { return false; } @@ -936,6 +1025,124 @@ static inline bool get_volume_mount_point_data(const char *filename, POOLMEM **d return true; } +/* + * Retrieve the symlink target + */ +static inline ssize_t get_symlink_data(const char *filename, POOLMEM *symlinktarget) +{ + ssize_t nrconverted = -1; + HANDLE h = INVALID_HANDLE_VALUE; + + if (p_GetFileAttributesW) { + POOLMEM *pwszBuf = get_pool_memory(PM_FNAME); + make_win32_path_UTF8_2_wchar(&pwszBuf, filename); + + 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); + } + + if (h == INVALID_HANDLE_VALUE) { + Dmsg1(dbglvl, "Invalid handle from CreateFileW(%s)\n", pwszBuf); + free_pool_memory(pwszBuf); + return -1; + } + + free_pool_memory(pwszBuf); + } else if (p_GetFileAttributesA) { + POOLMEM *win32_fname; + + win32_fname = get_pool_memory(PM_FNAME); + unix_name_to_win32(&win32_fname, filename); + h = CreateFileA(win32_fname, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, + NULL); + + if (h == INVALID_HANDLE_VALUE) { + Dmsg1(dbglvl, "Invalid handle from CreateFile(%s)\n", win32_fname); + free_pool_memory(win32_fname); + return -1; + } + free_pool_memory(win32_fname); + } + if (h != INVALID_HANDLE_VALUE) { + bool ok; + POOLMEM *buf; + int buf_length; + REPARSE_DATA_BUFFER *rdb; + DWORD bytes; + + /* + * Create a buffer big enough to hold all data. + */ + buf_length = sizeof(REPARSE_DATA_BUFFER) + MAX_PATH * sizeof(WCHAR); + buf = get_pool_memory(PM_NAME); + buf = check_pool_memory_size(buf, buf_length); + rdb = (REPARSE_DATA_BUFFER *)buf; + + memset(buf, 0, buf_length); + rdb->ReparseTag = IO_REPARSE_TAG_SYMLINK; + ok = DeviceIoControl(h, + FSCTL_GET_REPARSE_POINT, + NULL, + 0, /* in buffer, bytes */ + (LPVOID)buf, + (DWORD)buf_length, /* out buffer, btyes */ + (LPDWORD)&bytes, + (LPOVERLAPPED)0); + if (ok) { + POOLMEM *path; + int len, offset, ofs; + + len = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + offset = rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR); + + /* + * null-terminate pathbuffer + */ + *(wchar_t *)(rdb->SymbolicLinkReparseBuffer.PathBuffer + offset + len) = L'\0'; + + /* + * convert to UTF-8 + */ + path = get_pool_memory(PM_FNAME); + nrconverted = wchar_2_UTF8(path, (wchar_t *)(rdb->SymbolicLinkReparseBuffer.PathBuffer + offset)); + + ofs = 0; + if (bstrncasecmp(path, "\\??\\", 4)) { /* skip \\??\\ if exists */ + ofs = 4; + Dmsg0(dbglvl, "\\??\\ was in filename, skipping\n"); + } + + if (bstrncasecmp(path, "?\\", 2)) { /* skip ?\\ if exists, this is a MountPoint that we opened as Symlink */ + ofs = 2; + Dmsg0(dbglvl, "?\\ was in filename, skipping, use of MountPointReparseDataBuffer is needed\n"); + } + + pm_strcpy(symlinktarget, path + ofs); + free_pool_memory(path); + } else { + Dmsg1(dbglvl, "DeviceIoControl failed:%s\n", errorString()); + } + + CloseHandle(h); + free_pool_memory(buf); + } else { + return -1; + } + + return nrconverted; +} + /* * Encode file sttributes using the old Bacula method. */ @@ -1080,7 +1287,7 @@ static int get_windows_file_info(const char *filename, struct stat *sb, bool is_ */ sb->st_rdev = *pdwFileAttributes; if ((*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && - (*pdwReserved0 & IO_REPARSE_TAG_DEDUP)) { + (*pdwReserved0 & IO_REPARSE_TAG_DEDUP)) { sb->st_rdev |= FILE_ATTRIBUTES_DEDUPED_ITEM; } } @@ -1099,31 +1306,57 @@ static int get_windows_file_info(const char *filename, struct stat *sb, bool is_ * so it is like a Unix mount point (change of filesystem). */ if ((*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - if (*pdwReserved0 & IO_REPARSE_TAG_MOUNT_POINT) { + if (*pdwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) { + /* + * A mount point can be: + * Volume Mount Point "\\??\\volume{..." + * Junction Point "\\??\\..." + */ POOLMEM *vmp = get_pool_memory(PM_NAME); - if (get_volume_mount_point_data(filename, &vmp)) { - Dmsg2(dbglvl, "Junction %s points to: %s\n", filename, vmp); - - if (strncasecmp(vmp, "\\??\\volume{", 11) == 0) { + if (bstrncasecmp(vmp, "\\??\\volume{", 11)) { + Dmsg2(dbglvl, "Volume Mount Point %s points to: %s\n", filename, vmp); sb->st_rdev |= FILE_ATTRIBUTE_VOLUME_MOUNT_POINT; - sb->st_rdev &= ~FILE_ATTRIBUTES_JUNCTION_POINT; } else { - /* - * It points to a directory so we ignore it. - */ - sb->st_rdev |= FILE_ATTRIBUTES_JUNCTION_POINT; - sb->st_rdev &= ~FILE_ATTRIBUTE_VOLUME_MOUNT_POINT; + Dmsg2(dbglvl, "Junction Point %s points to: %s\n", filename, vmp); + sb->st_rdev |= FILE_ATTRIBUTES_JUNCTION_POINT; + sb->st_mode |= S_IFLNK; + sb->st_mode &= ~S_IFDIR; } - } else { - sb->st_rdev |= FILE_ATTRIBUTE_VOLUME_MOUNT_POINT; - sb->st_rdev &= ~FILE_ATTRIBUTES_JUNCTION_POINT; } free_pool_memory(vmp); + } else if (*pdwReserved0 == IO_REPARSE_TAG_SYMLINK) { + POOLMEM *slt; + + Dmsg0(dbglvl, "We have a symlinked directory!\n"); + sb->st_rdev |= FILE_ATTRIBUTES_SYMBOLIC_LINK; + sb->st_mode |= S_IFLNK; + sb->st_mode &= ~S_IFDIR; + + slt = get_pool_memory(PM_NAME); + slt = check_pool_memory_size(slt, MAX_PATH * sizeof(WCHAR)); + + if (get_symlink_data(filename, slt)) { + Dmsg2(dbglvl, "Symlinked Directory %s points to: %s\n", filename, slt); + } + free_pool_memory(slt); + } else { + Dmsg0(dbglvl, "IO_REPARSE_TAG_MOUNT_POINT with unhandled IO_REPARSE_TAG\n"); + } + } + } else { /* no directory */ + if ((*pdwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + if (*pdwReserved0 & IO_REPARSE_TAG_SYMLINK) { + Dmsg0(dbglvl, "We have a symlinked file!\n"); + sb->st_mode |= S_IFLNK; + + POOLMEM *slt = get_pool_memory(PM_NAME); + if (get_symlink_data(filename, slt)) { + Dmsg2(dbglvl, "Symlinked File %s points to: %s\n", filename, slt); + } + free_pool_memory(slt); } } - } else { - sb->st_mode |= S_IFREG; } Dmsg2(dbglvl, "st_rdev=%d filename=%s\n", sb->st_rdev, filename); @@ -1427,6 +1660,8 @@ int stat(const char *filename, struct stat *sb) return -1; } + /* TODO: Hardlinks ?*/ + sb->st_atime = cvt_ftime_to_utime(binfo.LastAccessTime); sb->st_mtime = cvt_ftime_to_utime(binfo.LastWriteTime); sb->st_ctime = cvt_ftime_to_utime(binfo.ChangeTime); @@ -1561,7 +1796,58 @@ int waitpid(int, int*, int) return -1; } -int readlink(const char *, char *, int) +/* + * Read contents of symbolic link + */ +ssize_t readlink(const char *path, char *buf, size_t bufsiz) +{ + Dmsg1(dbglvl, "readlink called for path %s\n", path); + get_symlink_data(path, buf); + return strlen(buf); +} + +/* + * Create a directory symlink / file symlink/junction + */ +int win32_symlink(const char *name1, const char *name2, _dev_t st_rdev) +{ +#if (_WIN32_WINNT >= 0x0600) + int dwFlags = 0x0; + + if (st_rdev & FILE_ATTRIBUTES_JUNCTION_POINT){ + Dmsg0(130, "We have a Junction Point \n"); + if (!CreateJunction(name2, name1)) { + return -1; + } else { + return 0; + } + } else if (st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT){ + Dmsg0(130, "We have a Volume Mount Point \n"); + return 0; + } else if (st_rdev & FILE_ATTRIBUTES_SYMBOLIC_LINK) { + Dmsg0(130, "We have a Directory Symbolic Link\n"); + dwFlags = SYMBOLIC_LINK_FLAG_DIRECTORY; + } else { + Dmsg0(130, "We have a File Symbolic Link \n"); + } + + Dmsg2(dbglvl, "symlink called name1=%s, name2=%s\n", name1, name2); + if (!CreateSymbolicLink(const_cast(name2), const_cast(name1), dwFlags)) { + Dmsg1(dbglvl, "CreateSymbolicLink failed:%s\n", errorString()); + return -1; + } else { + return 0; + } +#else + errno = ENOSYS; + return -1; +#endif +} + +/* + * Create a hardlink + */ +int link(const char *existing, const char *newfile) { errno = ENOSYS; return -1; diff --git a/src/win32/compat/include/compat.h b/src/win32/compat/include/compat.h index 73667c25c77..6a22d19bb47 100644 --- a/src/win32/compat/include/compat.h +++ b/src/win32/compat/include/compat.h @@ -184,6 +184,8 @@ struct stat #define S_IFIFO 0010000 /* pipe */ #undef S_IFREG #define S_IFREG 0100000 /* regular */ +#undef S_IFLNK +#define S_IFLNK 0120000 /* symbolic link */ #define S_IREAD 0000400 /* read permission, owner */ #define S_IWRITE 0000200 /* write permission, owner */ #define S_IEXEC 0000100 /* execute/search permission, owner */ @@ -192,6 +194,7 @@ struct stat #define S_IWUSR S_IWRITE #define S_IXUSR S_IEXEC #define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) +#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) #define S_ISCHR(x) 0 #define S_ISBLK(x) (((x) & S_IFMT) == S_IFBLK) @@ -211,7 +214,6 @@ struct stat #define S_ISGID 002000 #define S_ISVTX 001000 #define S_ISSOCK(x) 0 -#define S_ISLNK(x) 0 #if __STDC__ #define O_RDONLY _O_RDONLY @@ -273,13 +275,16 @@ int utime(const char *filename, struct utimbuf *buf); struct timespec; int readdir(unsigned int fd, struct dirent *dirp, unsigned int count); -int nanosleep(const struct timespec*, struct timespec *); +int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); long int random(void); void srandom(unsigned int seed); -int lstat(const char *, struct stat *); +int lstat(const char *filename, struct stat *sb); int stat(const char *file, struct stat *sb); -long pathconf(const char *, int); -int readlink(const char *, char *, int); +long pathconf(const char *path, int name); +ssize_t readlink(const char *path, char *buf, size_t bufsiz); +int win32_symlink(const char *name1, const char *name2, _dev_t st_rdev); +int link(const char *existing, const char *newfile); + #define _PC_PATH_MAX 1 #define _PC_NAME_MAX 2 @@ -406,6 +411,7 @@ int win32_ftruncate(int fd, int64_t length); #define FILE_ATTRIBUTE_VOLUME_MOUNT_POINT 0x10000000 #define FILE_ATTRIBUTES_JUNCTION_POINT 0x20000000 #define FILE_ATTRIBUTES_DEDUPED_ITEM 0x40000000 +#define FILE_ATTRIBUTES_SYMBOLIC_LINK 0x80000000 #ifndef IO_REPARSE_TAG_DEDUP #define IO_REPARSE_TAG_DEDUP (0x80000013)