Kept junctions points absolute or relative as specified

This commit is contained in:
jpandre 2009-12-15 09:25:36 +00:00
parent c5c51ec1fe
commit 2f8ced2ddd
5 changed files with 194 additions and 285 deletions

View File

@ -104,6 +104,8 @@ typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name,
extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
void *dirent, ntfs_filldir_t filldir);
ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni);
int ntfs_get_ntfs_dos_name(const char *path,
char *value, size_t size, ntfs_inode *ni);
int ntfs_set_ntfs_dos_name(const char *path,

View File

@ -24,8 +24,8 @@
#ifndef REPARSE_H
#define REPARSE_H
char *ntfs_make_symlink(const char *org_path,
ntfs_inode *ni, int *pattr_size);
char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point,
int *pattr_size);
BOOL ntfs_possible_symlink(ntfs_inode *ni);
int ntfs_get_ntfs_reparse_data(const char *path,

View File

@ -1840,6 +1840,45 @@ int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX));
}
/*
* Get a parent directory from an inode entry
*
* This is only used in situations where the path used to access
* the current file is not known for sure. The result may be different
* from the path when the file is linked in several parent directories.
*
* Currently this is only used for translating ".." in the target
* of a Vista relative symbolic link
*/
ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni)
{
ntfs_inode *dir_ni = (ntfs_inode*)NULL;
u64 inum;
FILE_NAME_ATTR *fn;
ntfs_attr_search_ctx *ctx;
if (ni->mft_no != FILE_root) {
/* find the name in the attributes */
ctx = ntfs_attr_get_search_ctx(ni, NULL);
if (!ctx)
return ((ntfs_inode*)NULL);
if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0,
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
/* We know this will always be resident. */
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
le16_to_cpu(ctx->attr->value_offset));
inum = le64_to_cpu(fn->parent_directory);
if (inum != (u64)-1) {
dir_ni = ntfs_inode_open(ni->vol, MREF(inum));
}
}
ntfs_attr_put_search_ctx(ctx);
}
return (dir_ni);
}
#ifdef HAVE_SETXATTR
#define MAX_DOS_NAME_LENGTH 12

View File

@ -89,12 +89,6 @@ struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */
char path_buffer[0]; /* above data assume this is char array */
} ;
struct INODE_STACK {
struct INODE_STACK *previous;
struct INODE_STACK *next;
ntfs_inode *ni;
} ;
struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */
INDEX_ENTRY_HEADER header;
REPARSE_INDEX_KEY key;
@ -205,9 +199,7 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname,
* name found :
* fix original name and return inode
*/
lemref = *(le64*)((char*)found->file_name
- sizeof(INDEX_ENTRY_HEADER)
- sizeof(FILE_NAME_ATTR));
lemref = entry->indexed_file;
mref = le64_to_cpu(lemref);
for (i=0; i<found->file_name_length; i++)
uname[i] = found->file_name[i];
@ -271,181 +263,90 @@ static char *search_absolute(ntfs_volume *vol, ntfschar *path,
return (target);
}
/*
* Stack the next inode in the path
*
* Returns the new top of stack
* or NULL (with stack unchanged) if there is a problem
*/
static struct INODE_STACK *stack_inode(struct INODE_STACK *topni,
ntfschar *name, int len, BOOL fix)
{
struct INODE_STACK *curni;
u64 inum;
if (fix)
inum = ntfs_fix_file_name(topni->ni, name, len);
else
inum = ntfs_inode_lookup_by_name(topni->ni, name, len);
if (inum != (u64)-1) {
inum = MREF(inum);
curni = (struct INODE_STACK*)malloc(sizeof(struct INODE_STACK));
if (curni) {
curni->ni = ntfs_inode_open(topni->ni->vol, inum);
topni->next = curni;
curni->previous = topni;
curni->next = (struct INODE_STACK*)NULL;
}
} else
curni = (struct INODE_STACK*)NULL;
return (curni);
}
/*
* Destack and close the current inode in the path
*
* Returns the new top of stack
* or NULL (with stack unchanged) if there is a problem
*/
static struct INODE_STACK *pop_inode(struct INODE_STACK *topni)
{
struct INODE_STACK *curni;
curni = (struct INODE_STACK*)NULL;
if (topni->previous) {
if (!ntfs_inode_close(topni->ni)) {
curni = topni->previous;
free(topni);
curni->next = (struct INODE_STACK*)NULL;
}
} else {
/* ".." reached the root of fs */
errno = ENOENT;
}
return (curni);
}
/*
* Search for a symbolic link along the target path,
* with the target defined as a relative path
*
* Note : the path used to access the current inode, may be
* different from the one implied in the target definition,
* when an inode has names in several directories.
*
* Returns the path translated to a Linux path
* or NULL if the path is not valid
*/
static char *search_relative(ntfs_volume *vol, ntfschar *path, int count,
const char *base, BOOL isdir)
static char *search_relative(ntfs_inode *ni, ntfschar *path, int count)
{
struct INODE_STACK *topni;
struct INODE_STACK *curni;
char *target;
ntfschar *unicode;
int unisz;
int start;
int len;
char *target = (char*)NULL;
ntfs_inode *curni;
ntfs_inode *newni;
u64 inum;
int pos;
int lth;
BOOL ok;
int max = 32; /* safety */
target = (char*)NULL; /* default return */
topni = (struct INODE_STACK*)malloc(sizeof(struct INODE_STACK));
if (topni) {
topni->ni = ntfs_inode_open(vol, FILE_root);
topni->previous = (struct INODE_STACK*)NULL;
topni->next = (struct INODE_STACK*)NULL;
}
if (topni && topni->ni) {
/*
* Process the base path
*/
unicode = (ntfschar*)NULL;
unisz = ntfs_mbstoucs(base, &unicode);
if ((unisz > 0) && unicode) {
start = 1;
do {
len = 0;
while (((start + len) < unisz)
&& (unicode[start + len]
!= const_cpu_to_le16('/')))
len++;
curni = (struct INODE_STACK*)NULL;
if ((start + len) < unisz) {
curni = stack_inode(topni,
&unicode[start], len, FALSE);
if (curni)
topni = curni;
} else
curni = topni;
start += len + 1;
} while (curni
&& topni->ni
&& (topni->ni->mrec->flags
& MFT_RECORD_IS_DIRECTORY)
&& (start < unisz));
free(unicode);
if (curni
&& topni->ni
&& (topni->ni->mrec->flags
& MFT_RECORD_IS_DIRECTORY)) {
start = 0;
do {
len = 0;
while (((start + len) < count)
&& (path[start + len]
!= const_cpu_to_le16('\\')))
len++;
curni = (struct INODE_STACK*)NULL;
if ((path[start]
== const_cpu_to_le16('.'))
&& ((len == 1)
|| ((len == 2)
&& (path[start+1]
== const_cpu_to_le16('.'))))) {
/* leave the .. or . in the path */
curni = topni;
if (len == 2) {
curni = pop_inode(topni);
if (curni)
topni = curni;
}
} else {
curni = stack_inode(topni,
&path[start], len, TRUE);
if (curni)
topni = curni;
}
if (topni->ni) {
start += len;
if (start < count)
path[start++]
= const_cpu_to_le16('/');
}
} while (curni
&& topni->ni
&& (topni->ni->mrec->flags
& MFT_RECORD_IS_DIRECTORY)
&& (start < count));
if (curni
&& topni->ni
&& (topni->ni->mrec->flags
& MFT_RECORD_IS_DIRECTORY
? isdir : !isdir)) {
if (ntfs_ucstombs(path, count,
&target, 0) < 0) {
if (target) {
free(target);
target = (char*)NULL;
pos = 0;
ok = TRUE;
curni = ntfs_dir_parent_inode(ni);
while (curni && ok && (pos < (count - 1)) && --max) {
if ((count >= (pos + 2))
&& (path[pos] == const_cpu_to_le16('.'))
&& (path[pos+1] == const_cpu_to_le16('\\'))) {
path[1] = const_cpu_to_le16('/');
pos += 2;
} else {
if ((count >= (pos + 3))
&& (path[pos] == const_cpu_to_le16('.'))
&&(path[pos+1] == const_cpu_to_le16('.'))
&& (path[pos+2] == const_cpu_to_le16('\\'))) {
path[2] = const_cpu_to_le16('/');
pos += 3;
newni = ntfs_dir_parent_inode(curni);
if (curni != ni)
ntfs_inode_close(curni);
curni = newni;
if (!curni)
ok = FALSE;
} else {
lth = 0;
while (((pos + lth) < count)
&& (path[pos + lth] != const_cpu_to_le16('\\')))
lth++;
if (lth > 0)
inum = ntfs_fix_file_name(curni,&path[pos],lth);
else
inum = (u64)-1;
if (!lth
|| ((curni != ni)
&& ntfs_inode_close(curni))
|| (inum == (u64)-1))
ok = FALSE;
else {
curni = ntfs_inode_open(ni->vol, MREF(inum));
if (!curni)
ok = FALSE;
else {
if (ok && ((pos + lth) < count)) {
path[pos + lth] = const_cpu_to_le16('/');
pos += lth + 1;
} else {
pos += lth;
if ((ni->mrec->flags ^ curni->mrec->flags)
& MFT_RECORD_IS_DIRECTORY)
ok = FALSE;
if (ntfs_inode_close(curni))
ok = FALSE;
}
}
}
}
}
do {
if (topni->ni)
ntfs_inode_close(topni->ni);
curni = topni;
topni = topni->previous;
free(curni);
} while (topni);
}
if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) {
free(target); // needed ?
target = (char*)NULL;
}
return (target);
}
@ -507,16 +408,12 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter)
* or NULL if there were some problem, as described by errno
*/
static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
int count, const char *path, BOOL isdir)
int count, const char *mnt_point, BOOL isdir)
{
char *target;
char *fulltarget;
int i;
int sz;
int level;
const char *p;
char *q;
enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind;
@ -554,19 +451,11 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
&& !ntfs_drive_letter(vol, junction[4])) {
target = search_absolute(vol,&junction[7],count - 7, isdir);
if (target) {
level = 0;
for (p=path; *p; p++)
if (*p == '/')
level++;
fulltarget = (char*)ntfs_malloc(3*level
+ strlen(target) + 1);
fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
+ strlen(target) + 2);
if (fulltarget) {
fulltarget[0] = 0;
if (level > 1) {
for (i=1; i<level; i++)
strcat(fulltarget,"../");
} else
strcpy(fulltarget,"./");
strcpy(fulltarget,mnt_point);
strcat(fulltarget,"/");
strcat(fulltarget,target);
}
free(target);
@ -593,19 +482,11 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
&& (target[0] >= 'a')
&& (target[0] <= 'z'))
target[0] += 'A' - 'a';
level = 0;
for (p=path; *p; p++)
if (*p == '/')
level++;
fulltarget = (char*)ntfs_malloc(3*level
+ sizeof(mappingdir) + strlen(target) - 3);
fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
+ sizeof(mappingdir) + strlen(target) + 1);
if (fulltarget) {
fulltarget[0] = 0;
if (level > 1) {
for (i=1; i<level; i++)
strcat(fulltarget,"../");
} else
strcpy(fulltarget,"./");
strcpy(fulltarget,mnt_point);
strcat(fulltarget,"/");
strcat(fulltarget,mappingdir);
strcat(fulltarget,target);
}
@ -631,14 +512,11 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction,
*/
static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
int count, const char *path, BOOL isdir)
int count, const char *mnt_point, BOOL isdir)
{
char *target;
char *fulltarget;
int i;
int sz;
int level;
const char *p;
char *q;
enum { FULL_PATH, ABS_PATH, REJECTED_PATH } kind;
@ -680,19 +558,11 @@ static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
target = search_absolute(vol, &junction[3],
count - 3, isdir);
if (target) {
level = 0;
for (p=path; *p; p++)
if (*p == '/')
level++;
fulltarget = (char*)ntfs_malloc(3*level
+ strlen(target) + 1);
fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
+ strlen(target) + 2);
if (fulltarget) {
fulltarget[0] = 0;
if (level > 1) {
for (i=1; i<level; i++)
strcat(fulltarget,"../");
} else
strcpy(fulltarget,"./");
strcpy(fulltarget,mnt_point);
strcat(fulltarget,"/");
strcat(fulltarget,target);
}
free(target);
@ -716,19 +586,11 @@ static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
&& (target[0] >= 'a')
&& (target[0] <= 'z'))
target[0] += 'A' - 'a';
level = 0;
for (p=path; *p; p++)
if (*p == '/')
level++;
fulltarget = (char*)ntfs_malloc(3*level
+ sizeof(mappingdir) + strlen(target) - 3);
fulltarget = (char*)ntfs_malloc(strlen(mnt_point)
+ sizeof(mappingdir) + strlen(target) + 1);
if (fulltarget) {
fulltarget[0] = 0;
if (level > 1) {
for (i=1; i<level; i++)
strcat(fulltarget,"../");
} else
strcpy(fulltarget,"./");
strcpy(fulltarget,mnt_point);
strcat(fulltarget,"/");
strcat(fulltarget,mappingdir);
strcat(fulltarget,target);
}
@ -751,12 +613,11 @@ static char *ntfs_get_abslink(ntfs_volume *vol, ntfschar *junction,
* or NULL if there were some problem, as described by errno
*/
static char *ntfs_get_rellink(ntfs_volume *vol, ntfschar *junction,
int count, const char *path, BOOL isdir)
static char *ntfs_get_rellink(ntfs_inode *ni, ntfschar *junction, int count)
{
char *target;
target = search_relative(vol,junction,count,path,isdir);
target = search_relative(ni,junction,count);
return (target);
}
@ -770,8 +631,8 @@ static char *ntfs_get_rellink(ntfs_volume *vol, ntfschar *junction,
* symbolic link or directory junction
*/
char *ntfs_make_symlink(const char *org_path,
ntfs_inode *ni, int *pattr_size)
char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point,
int *pattr_size)
{
s64 attr_size = 0;
char *target;
@ -800,19 +661,12 @@ char *ntfs_make_symlink(const char *org_path,
reparse_attr->reparse_data;
offs = le16_to_cpu(mount_point_data->subst_name_offset);
lth = le16_to_cpu(mount_point_data->subst_name_length);
/* consistency checks */
if (isdir
&& ((le16_to_cpu(reparse_attr->reparse_data_length)
+ 8) == attr_size)
&& ((int)((sizeof(REPARSE_POINT)
+ sizeof(struct MOUNT_POINT_REPARSE_DATA)
+ offs + lth)) <= attr_size)) {
target = ntfs_get_fulllink(vol,
(ntfschar*)&mount_point_data->path_buffer[offs],
lth/2, org_path, isdir);
if (target)
bad = FALSE;
}
/* reparse data consistency has been checked */
target = ntfs_get_fulllink(vol,
(ntfschar*)&mount_point_data->path_buffer[offs],
lth/2, mnt_point, isdir);
if (target)
bad = FALSE;
break;
case IO_REPARSE_TAG_SYMLINK :
symlink_data = (struct SYMLINK_REPARSE_DATA*)
@ -836,44 +690,37 @@ char *ntfs_make_symlink(const char *org_path,
else
kind = REL_TARGET;
p--;
/* consistency checks */
if (((le16_to_cpu(reparse_attr->reparse_data_length)
+ 8) == attr_size)
&& ((int)((sizeof(REPARSE_POINT)
+ sizeof(struct SYMLINK_REPARSE_DATA)
+ offs + lth)) <= attr_size)) {
switch (kind) {
case FULL_TARGET :
if (!(symlink_data->flags
& const_cpu_to_le32(1))) {
target = ntfs_get_fulllink(vol,
p, lth/2,
org_path, isdir);
if (target)
bad = FALSE;
}
break;
case ABS_TARGET :
if (symlink_data->flags
& const_cpu_to_le32(1)) {
target = ntfs_get_abslink(vol,
p, lth/2,
org_path, isdir);
if (target)
bad = FALSE;
}
break;
case REL_TARGET :
if (symlink_data->flags
& const_cpu_to_le32(1)) {
target = ntfs_get_rellink(vol,
p, lth/2,
org_path, isdir);
if (target)
bad = FALSE;
}
break;
/* reparse data consistency has been checked */
switch (kind) {
case FULL_TARGET :
if (!(symlink_data->flags
& const_cpu_to_le32(1))) {
target = ntfs_get_fulllink(vol,
p, lth/2,
mnt_point, isdir);
if (target)
bad = FALSE;
}
break;
case ABS_TARGET :
if (symlink_data->flags
& const_cpu_to_le32(1)) {
target = ntfs_get_abslink(vol,
p, lth/2,
mnt_point, isdir);
if (target)
bad = FALSE;
}
break;
case REL_TARGET :
if (symlink_data->flags
& const_cpu_to_le32(1)) {
target = ntfs_get_rellink(ni,
p, lth/2);
if (target)
bad = FALSE;
}
break;
}
break;
}

View File

@ -165,6 +165,7 @@ typedef struct {
BOOL inherit;
unsigned int secure_flags;
char *usermap_path;
char *abs_mnt_point;
struct PERMISSIONS_CACHE *seccache;
struct SECURITY_CONTEXT security;
} ntfs_fuse_context_t;
@ -689,7 +690,7 @@ static int ntfs_fuse_getattr(const char *org_path, struct stat *stbuf)
int attr_size;
errno = 0;
target = ntfs_make_symlink(org_path, ni, &attr_size);
target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
/*
* If the reparse point is not a valid
* directory junction, and there is no error
@ -870,7 +871,7 @@ static int ntfs_fuse_readlink(const char *org_path, char *buf, size_t buf_size)
errno = 0;
res = 0;
target = ntfs_make_symlink(org_path, ni, &attr_size);
target = ntfs_make_symlink(ni, ctx->abs_mnt_point, &attr_size);
if (target) {
strncpy(buf,target,buf_size);
free(target);
@ -4105,6 +4106,24 @@ int main(int argc, char *argv[])
goto err_out;
}
/* need absolute mount point for junctions */
if (opts.mnt_point[0] == '/')
ctx->abs_mnt_point = strdup(opts.mnt_point);
else {
ctx->abs_mnt_point = (char*)ntfs_malloc(PATH_MAX);
if (ctx->abs_mnt_point) {
if (getcwd(ctx->abs_mnt_point,
PATH_MAX - strlen(opts.mnt_point) - 1)) {
strcat(ctx->abs_mnt_point, "/");
strcat(ctx->abs_mnt_point, opts.mnt_point);
}
}
}
if (!ctx->abs_mnt_point) {
err = NTFS_VOLUME_OUT_OF_MEMORY;
goto err_out;
}
ctx->security.uid = 0;
ctx->security.gid = 0;
if ((opts.mnt_point[0] == '/')
@ -4227,6 +4246,8 @@ int main(int argc, char *argv[])
fuse_destroy(fh);
err_out:
ntfs_mount_error(opts.device, opts.mnt_point, err);
if (ctx->abs_mnt_point)
free(ctx->abs_mnt_point);
err2:
ntfs_close();
free(ctx);