Skip to content

Commit

Permalink
We now support symlink-like objects on windows
Browse files Browse the repository at this point in the history
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 <philipp.storz@bareos.com>
Signed-off-by: Marco van Wieringen <marco.van.wieringen@bareos.com>
  • Loading branch information
Oleg Livshyts authored and Marco van Wieringen committed Feb 17, 2015
1 parent b619962 commit 8184f63
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 75 deletions.
11 changes: 7 additions & 4 deletions src/filed/backup.c
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down
50 changes: 39 additions & 11 deletions src/findlib/create_file.c
Expand Up @@ -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
*
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 */

Expand All @@ -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.
Expand Down
8 changes: 3 additions & 5 deletions src/findlib/find_one.c
Expand Up @@ -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
Expand All @@ -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;
}

Expand Down
51 changes: 32 additions & 19 deletions src/findlib/mkpath.c
Expand Up @@ -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,
Expand Down Expand Up @@ -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:\\";

Expand All @@ -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++;
}
Expand All @@ -282,21 +282,30 @@ 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;
}
if (ndir >= max_dirs) {
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] == ':') {
Expand All @@ -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++;
}
Expand All @@ -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);
}
Expand Down
6 changes: 0 additions & 6 deletions src/lib/attr.c
Expand Up @@ -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 &&
Expand Down

0 comments on commit 8184f63

Please sign in to comment.