From 464eab4178b5aa69727fedd3c9f19a1954849e56 Mon Sep 17 00:00:00 2001 From: cha0smaster Date: Sat, 24 Sep 2005 15:20:49 +0000 Subject: [PATCH] add ntfs_link, update ntfsmount to use it --- include/ntfs/dir.h | 2 + libntfs/dir.c | 93 +++++++++++++++++++++++++++++++++++++++++++ ntfsprogs/ntfsmount.c | 49 +++++++++++++++++++++++ 3 files changed, 144 insertions(+) diff --git a/include/ntfs/dir.h b/include/ntfs/dir.h index ddf624b3..98b1b256 100644 --- a/include/ntfs/dir.h +++ b/include/ntfs/dir.h @@ -44,6 +44,8 @@ extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len, const unsigned type); extern int ntfs_delete(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len); +extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, + u8 name_len); /* * File types (adapted from include ) diff --git a/libntfs/dir.c b/libntfs/dir.c index 5e8a7d12..0393f69f 100644 --- a/libntfs/dir.c +++ b/libntfs/dir.c @@ -1406,3 +1406,96 @@ err_out: err = errno; goto out; } + +/** + * ntfs_link - create hard link for file or directory + * @ni: ntfs inode for object to create hard link + * @dir_ni: ntfs inode for directory in which new link should be placed + * @name: unicode name of the new link + * @name_len: length of the name in unicode characters + * + * Return 0 on success or -1 on error with errno set to the error code. + */ +int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) +{ + FILE_NAME_ATTR *fn = NULL; + int fn_len, err; + + ntfs_debug("Entering."); + if (!ni || !dir_ni || !name || !name_len) { + err = errno; + ntfs_error(, "Invalid arguments."); + goto err_out; + } + /* Create FILE_NAME attribute. */ + fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar); + fn = calloc(1, fn_len); + if (!fn) { + err = errno; + ntfs_error(, "Not enough memory."); + goto err_out; + } + fn->parent_directory = MK_LE_MREF(dir_ni->mft_no, + le16_to_cpu(dir_ni->mrec->sequence_number)); + fn->file_name_length = name_len; + fn->file_name_type = FILE_NAME_POSIX; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fn->file_attributes = FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT; + fn->creation_time = utc2ntfs(ni->creation_time); + fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); + fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); + fn->last_access_time = utc2ntfs(ni->last_access_time); + memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); + /* Add FILE_NAME attribute to index. */ + if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, + le16_to_cpu(ni->mrec->sequence_number)))) { + err = errno; + ntfs_error(, "Failed to add entry to the index."); + goto err_out; + } + /* Add FILE_NAME attribute to inode. */ + if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { + ntfs_index_context *ictx; + + err = errno; + ntfs_error(, "Failed to add FILE_NAME attribute."); + /* Try to remove just added attribute from index. */ + ictx = ntfs_index_ctx_get(dir_ni, I30, 4); + if (!ictx) + goto rollback_failed; + if (ntfs_index_lookup(fn, fn_len, ictx)) { + ntfs_index_ctx_put(ictx); + goto rollback_failed; + } + if (ntfs_index_rm(ictx)) { + ntfs_index_ctx_put(ictx); + goto rollback_failed; + } + goto err_out; + } + /* Increment hard links count. */ + ni->mrec->link_count = cpu_to_le16(le16_to_cpu( + ni->mrec->link_count) + 1); + /* + * Do not set attributes and file size, instead of this mark filenames + * dirty to force attribute and size update during sync. + * NOTE: File size may will be not updated and not all attributes will + * be set, but it is acceptable since windows driver does not update + * all file names when one of the hard links changed. + * FIXME: It will be nice to update them all. + */ + NInoFileNameSetDirty(ni); + /* Done! */ + ntfs_inode_mark_dirty(ni); + free(fn); + ntfs_debug("Done."); + return 0; +rollback_failed: + ntfs_error(, "Rollback failed. Leaving inconsist metadata."); +err_out: + ntfs_error(, "Failed."); + if (fn) + free(fn); + errno = err; + return -1; +} diff --git a/ntfsprogs/ntfsmount.c b/ntfsprogs/ntfsmount.c index 9a720093..622b58c7 100644 --- a/ntfsprogs/ntfsmount.c +++ b/ntfsprogs/ntfsmount.c @@ -628,6 +628,54 @@ static int ntfs_fuse_mknod(const char *org_path, mode_t mode, return res; } +static int ntfs_fuse_link(const char *old_path, const char *new_path) +{ + char *name; + ntfschar *uname = NULL; + ntfs_inode *dir_ni = NULL, *ni; + char *path; + int res = 0, uname_len; + + path = strdup(new_path); + if (!path) + return -errno; + /* Open file for which create hard link. */ + ni = ntfs_pathname_to_inode(ctx->vol, NULL, old_path); + if (!ni) { + res = -errno; + goto exit; + } + /* Generate unicode filename. */ + name = strrchr(path, '/'); + name++; + uname_len = ntfs_mbstoucs(name, &uname, 0); + if (uname_len < 0) { + res = -errno; + goto exit; + } + /* Open parent directory. */ + *name = 0; + dir_ni = ntfs_pathname_to_inode(ctx->vol, NULL, path); + if (!dir_ni) { + res = -errno; + if (res == -ENOENT) + res = -EIO; + goto exit; + } + /* Create hard link. */ + if (ntfs_link(ni, dir_ni, uname, uname_len)) + res = -errno; +exit: + if (ni) + ntfs_inode_close(ni); + if (uname) + free(uname); + if (dir_ni) + ntfs_inode_close(dir_ni); + free(path); + return res; +} + static int ntfs_fuse_rm(const char *org_path) { char *name; @@ -1042,6 +1090,7 @@ static struct fuse_operations ntfs_fuse_oper = { .statfs = ntfs_fuse_statfs, .chmod = ntfs_fuse_chmod, .mknod = ntfs_fuse_mknod, + .link = ntfs_fuse_link, .unlink = ntfs_fuse_unlink, .mkdir = ntfs_fuse_mkdir, .rmdir = ntfs_fuse_rmdir,