diff --git a/include/ntfs-3g/dir.h b/include/ntfs-3g/dir.h index 49f706fb..796e61ff 100644 --- a/include/ntfs-3g/dir.h +++ b/include/ntfs-3g/dir.h @@ -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, diff --git a/include/ntfs-3g/reparse.h b/include/ntfs-3g/reparse.h index 91ede03e..730d9fdb 100644 --- a/include/ntfs-3g/reparse.h +++ b/include/ntfs-3g/reparse.h @@ -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, diff --git a/libntfs-3g/dir.c b/libntfs-3g/dir.c index 8dd83c93..47ced720 100644 --- a/libntfs-3g/dir.c +++ b/libntfs-3g/dir.c @@ -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 diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c index 69e8f0c6..7edc4561 100644 --- a/libntfs-3g/reparse.c +++ b/libntfs-3g/reparse.c @@ -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; ifile_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= '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 1) { - for (i=1; i= '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; ireparse_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; } diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index 2cc1622a..8d1015b7 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -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);