Commit 9430ff7c authored by Erich E. Hoover's avatar Erich E. Hoover Committed by Vitaly Lipatov

server: Rewrite absolute reparse point targets if the prefix location changes.

parent 534b9d94
......@@ -1906,23 +1906,16 @@ void get_nt_name( struct fd *fd, struct unicode_str *name )
name->len = fd->nt_namelen;
}
int check_symlink( char *name )
static char *decode_symlink(const char *name, ULONG *tag, int *is_dir)
{
struct stat st;
lstat( name, &st );
return S_ISLNK( st.st_mode );
}
static void decode_symlink(char *name, int *is_dir)
{
char link[MAX_PATH], *p;
static char link[MAX_PATH];
ULONG reparse_tag;
int len, i;
char *p;
len = readlink( name, link, sizeof(link) );
if (len == -1)
return;
return NULL;
link[len] = 0;
p = link;
/* skip past relative/absolute indication */
......@@ -1930,7 +1923,7 @@ static void decode_symlink(char *name, int *is_dir)
p++;
if (*p++ != '/')
{
return;
return NULL;
}
/* decode the reparse tag */
reparse_tag = 0;
......@@ -1944,7 +1937,7 @@ static void decode_symlink(char *name, int *is_dir)
else if (c == '.' && *p++ == '/')
val = 1;
else
return;
return NULL;
reparse_tag |= (val << i);
}
/* decode the directory/file flag */
......@@ -1957,10 +1950,70 @@ static void decode_symlink(char *name, int *is_dir)
else if (c == '.' && *p++ == '/')
*is_dir = TRUE;
else
return;
return NULL;
}
else
*is_dir = TRUE;
if (tag) *tag = reparse_tag;
return p;
}
static int rewrite_symlink( const char *path )
{
static char marker[] = "////.//.//"; /* "P" (0x50) encoded as a path (0=/ 1=./) */
char *link, *prefix_end, *local_link;
static char config_dir[MAX_PATH];
static int config_dir_len = 0;
char new_target[PATH_MAX];
int len, is_dir, i;
ULONG tag;
/* obtain the wine prefix path */
if (!config_dir_len)
{
char tmp_dir[MAX_PATH];
if (getcwd( tmp_dir, sizeof(tmp_dir) ) == NULL) return FALSE;
if (fchdir( config_dir_fd ) == -1) return FALSE;
if (getcwd( config_dir, sizeof(config_dir) ) == NULL) return FALSE;
if (chdir( tmp_dir ) == -1) return FALSE;
config_dir_len = strlen( config_dir );
}
/* grab the current link contents */
link = decode_symlink( path, &tag, &is_dir );
if (link == NULL) return FALSE;
/* find out if the prefix matches, if it does then do not modify the link */
prefix_end = strstr( link, marker );
if (prefix_end == NULL) return TRUE;
local_link = prefix_end + strlen( marker );
len = prefix_end - link;
if (len == config_dir_len && strncmp( config_dir, link, len ) == 0) return TRUE;
/* if the prefix does not match then re-encode the link with the new prefix */
/* Encode the reparse tag into the symlink */
strcpy( new_target, "/" );
for (i = 0; i < sizeof(ULONG)*8; i++)
{
if ((tag >> i) & 1)
strcat( new_target, "." );
strcat( new_target, "/" );
}
/* Encode the type (file or directory) if NT symlink */
if (tag == IO_REPARSE_TAG_SYMLINK)
{
if (is_dir)
strcat( new_target, "." );
strcat( new_target, "/" );
}
strcat( new_target, config_dir );
strcat( new_target, marker );
strcat( new_target, local_link );
/* replace the symlink */
unlink( path );
symlink( new_target, path );
return TRUE;
}
/* open() wrapper that returns a struct fd with no fd user set */
......@@ -2026,7 +2079,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
fd->unix_name = NULL;
if ((path = dup_fd_name( root, name )))
{
int is_symlink = check_symlink( path );
int is_symlink = rewrite_symlink( path );
#if defined(O_SYMLINK)
if (is_symlink && (options & FILE_OPEN_REPARSE_POINT) && !(flags & O_CREAT))
flags |= O_SYMLINK;
......@@ -2090,7 +2143,7 @@ struct fd *open_fd( struct fd *root, const char *name, struct unicode_str nt_nam
*mode = st.st_mode;
is_dir = S_ISDIR(st.st_mode);
if (is_link)
decode_symlink(fd->unlink_name, &is_dir);
decode_symlink(fd->unlink_name, NULL, &is_dir);
/* check directory options */
if ((options & FILE_DIRECTORY_FILE) && !is_dir)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment