static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name, u32 name_len, ntfs_attr_search_ctx **ctx) { ATTR_RECORD *a; INDEX_ROOT *ir = NULL; ntfs_log_trace("Entering\n"); *ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!*ctx) return NULL; if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE, 0, NULL, 0, *ctx)) { ntfs_log_perror("Failed to lookup $INDEX_ROOT"); goto err_out; } a = (*ctx)->attr; if (a->non_resident) { errno = EINVAL; ntfs_log_perror("Non-resident $INDEX_ROOT detected"); goto err_out; } ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->value_offset)); err_out: if (!ir) { ntfs_attr_put_search_ctx(*ctx); *ctx = NULL; } return ir; }
status_t fs_rewind_attrib_dir(fs_volume *_vol, fs_vnode *_node, void *_cookie) { nspace *ns = (nspace*)_vol->private_volume; attrdircookie *cookie = (attrdircookie *)_cookie; status_t result = B_NO_ERROR; TRACE("%s - ENTER\n", __FUNCTION__); LOCK_VOL(ns); if (cookie->ctx) ntfs_attr_put_search_ctx(cookie->ctx); cookie->ctx = ntfs_attr_get_search_ctx(cookie->inode, NULL); if (cookie->ctx == NULL) { result = errno; //goto exit; } //exit: TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
status_t fs_open_attrib_dir(fs_volume *_vol, fs_vnode *_node, void **_cookie) { nspace *ns = (nspace*)_vol->private_volume; vnode *node = (vnode*)_node->private_node; attrdircookie *cookie = NULL; ntfs_inode *ni = NULL; ntfs_attr_search_ctx *ctx = NULL; status_t result = B_NO_ERROR; TRACE("%s - ENTER\n", __FUNCTION__); LOCK_VOL(ns); ni = ntfs_inode_open(ns->ntvol, node->vnid); if (ni == NULL) { result = errno; goto exit; } ctx = ntfs_attr_get_search_ctx(ni, NULL); if (ctx == NULL) { result = errno; goto exit; } cookie = (attrdircookie*)ntfs_calloc(sizeof(attrdircookie)); if (cookie == NULL) { result = ENOMEM; goto exit; } cookie->inode = ni; cookie->ctx = ctx; ni = NULL; ctx = NULL; *_cookie = cookie; exit: if (ctx) ntfs_attr_put_search_ctx(ctx); if (ni) ntfs_inode_close(ni); TRACE("%s - EXIT, result is %s\n", __FUNCTION__, strerror(result)); UNLOCK_VOL(ns); return result; }
static struct dentry *ntfs_get_parent(struct dentry *child_dent) { struct inode *vi = child_dent->d_inode; ntfs_inode *ni = NTFS_I(vi); MFT_RECORD *mrec; ntfs_attr_search_ctx *ctx; ATTR_RECORD *attr; FILE_NAME_ATTR *fn; unsigned long parent_ino; int err; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); mrec = map_mft_record(ni); if (IS_ERR(mrec)) return (struct dentry *)mrec; ctx = ntfs_attr_get_search_ctx(ni, mrec); if (unlikely(!ctx)) { unmap_mft_record(ni); return ERR_PTR(-ENOMEM); } try_next: err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); if (err == -ENOENT) ntfs_error(vi->i_sb, "Inode 0x%lx does not have a " "file name attribute. Run chkdsk.", vi->i_ino); return ERR_PTR(err); } attr = ctx->attr; if (unlikely(attr->non_resident)) goto try_next; fn = (FILE_NAME_ATTR *)((u8 *)attr + le16_to_cpu(attr->data.resident.value_offset)); if (unlikely((u8 *)fn + le32_to_cpu(attr->data.resident.value_length) > (u8*)attr + le32_to_cpu(attr->length))) goto try_next; parent_ino = MREF_LE(fn->parent_directory); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); return d_obtain_alias(ntfs_iget(vi->i_sb, parent_ino)); }
/** * ntfs_inode_sync_standard_information - update standard information attribute * @ni: ntfs inode to update standard information * * Return 0 on success or -1 on error with errno set to the error code. */ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; u32 lth; le32 lthle; ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) return -1; if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Failed to sync standard info (inode %lld)", (long long)ni->mft_no); ntfs_attr_put_search_ctx(ctx); return -1; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); std_info->file_attributes = ni->flags; if (test_nino_flag(ni, TimesDirty)) { std_info->creation_time = utc2ntfs(ni->creation_time); std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time); std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); std_info->last_access_time = utc2ntfs(ni->last_access_time); } /* JPA update v3.x extensions, ensuring consistency */ lthle = ctx->attr->length; lth = le32_to_cpu(lthle); if (test_nino_flag(ni, v3_Extensions) && (lth <= sizeof(STANDARD_INFORMATION))) ntfs_log_error("bad sync of standard information\n"); if (lth > sizeof(STANDARD_INFORMATION)) { std_info->owner_id = ni->owner_id; std_info->security_id = ni->security_id; std_info->quota_charged = ni->quota_charged; std_info->usn = ni->usn; } ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); return 0; }
int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; u64 *times; int ret; ret = 0; ctx = ntfs_attr_get_search_ctx(ni, NULL); if (ctx) { if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Failed to get standard info (inode %lld)", (long long)ni->mft_no); } else { std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if (value && (size >= 8)) { times = (u64*)value; times[0] = le64_to_cpu(std_info->creation_time); ret = 8; if (size >= 16) { times[1] = le64_to_cpu(std_info->last_data_change_time); ret = 16; } if (size >= 24) { times[2] = le64_to_cpu(std_info->last_access_time); ret = 24; } if (size >= 32) { times[3] = le64_to_cpu(std_info->last_mft_change_time); ret = 32; } } else if (!size) ret = 32; else ret = -ERANGE; } ntfs_attr_put_search_ctx(ctx); } return (ret ? ret : -errno); }
/** * dump_file */ static int dump_file(ntfs_volume *vol, ntfs_inode *ino) { char buffer[1024]; ntfs_attr_search_ctx *ctx; ATTR_RECORD *rec; int i; runlist *runs; utils_inode_get_name(ino, buffer, sizeof(buffer)); ntfs_log_info("Dump: %s\n", buffer); ctx = ntfs_attr_get_search_ctx(ino, NULL); while ((rec = find_attribute(AT_UNUSED, ctx))) { ntfs_log_info(" 0x%02x - ", rec->type); if (rec->non_resident) { ntfs_log_info("non-resident\n"); runs = ntfs_mapping_pairs_decompress(vol, rec, NULL); if (runs) { ntfs_log_info(" VCN LCN Length\n"); for (i = 0; runs[i].length > 0; i++) { ntfs_log_info(" %8lld %8lld %8lld\n", (long long)runs[i].vcn, (long long)runs[i].lcn, (long long) runs[i].length); } free(runs); } } else { ntfs_log_info("resident\n"); } } ntfs_attr_put_search_ctx(ctx); return 0; }
/** * ntfs_get_parent - find the dentry of the parent of a given directory dentry * @child_dent: dentry of the directory whose parent directory to find * * Find the dentry for the parent directory of the directory specified by the * dentry @child_dent. This function is called from * fs/exportfs/expfs.c::find_exported_dentry() which in turn is called from the * default ->decode_fh() which is export_decode_fh() in the same file. * * The code is based on the ext3 ->get_parent() implementation found in * fs/ext3/namei.c::ext3_get_parent(). * * Note: ntfs_get_parent() is called with @child_dent->d_inode->i_mutex down. * * Return the dentry of the parent directory on success or the error code on * error (IS_ERR() is true). */ static struct dentry *ntfs_get_parent(struct dentry *child_dent) { struct inode *vi = child_dent->d_inode; ntfs_inode *ni = NTFS_I(vi); MFT_RECORD *mrec; ntfs_attr_search_ctx *ctx; ATTR_RECORD *attr; FILE_NAME_ATTR *fn; struct inode *parent_vi; struct dentry *parent_dent; unsigned long parent_ino; int err; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); /* Get the mft record of the inode belonging to the child dentry. */ mrec = map_mft_record(ni); if (IS_ERR(mrec)) return (struct dentry *)mrec; /* Find the first file name attribute in the mft record. */ ctx = ntfs_attr_get_search_ctx(ni, mrec); if (unlikely(!ctx)) { unmap_mft_record(ni); return ERR_PTR(-ENOMEM); } try_next: err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); if (err == -ENOENT) ntfs_error(vi->i_sb, "Inode 0x%lx does not have a " "file name attribute. Run chkdsk.", vi->i_ino); return ERR_PTR(err); } attr = ctx->attr; if (unlikely(attr->non_resident)) goto try_next; fn = (FILE_NAME_ATTR *)((u8 *)attr + le16_to_cpu(attr->data.resident.value_offset)); if (unlikely((u8 *)fn + le32_to_cpu(attr->data.resident.value_length) > (u8*)attr + le32_to_cpu(attr->length))) goto try_next; /* Get the inode number of the parent directory. */ parent_ino = MREF_LE(fn->parent_directory); /* Release the search context and the mft record of the child. */ ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); /* Get the inode of the parent directory. */ parent_vi = ntfs_iget(vi->i_sb, parent_ino); if (IS_ERR(parent_vi) || unlikely(is_bad_inode(parent_vi))) { if (!IS_ERR(parent_vi)) iput(parent_vi); ntfs_error(vi->i_sb, "Failed to get parent directory inode " "0x%lx of child inode 0x%lx.", parent_ino, vi->i_ino); return ERR_PTR(-EACCES); } /* Finally get a dentry for the parent directory and return it. */ parent_dent = d_alloc_anon(parent_vi); if (unlikely(!parent_dent)) { iput(parent_vi); return ERR_PTR(-ENOMEM); } ntfs_debug("Done for inode 0x%lx.", vi->i_ino); return parent_dent; }
/** * ntfs_lookup - find the inode represented by a dentry in a directory inode * @dir_ino: directory inode in which to look for the inode * @dent: dentry representing the inode to look for * @nd: lookup nameidata * * In short, ntfs_lookup() looks for the inode represented by the dentry @dent * in the directory inode @dir_ino and if found attaches the inode to the * dentry @dent. * * In more detail, the dentry @dent specifies which inode to look for by * supplying the name of the inode in @dent->d_name.name. ntfs_lookup() * converts the name to Unicode and walks the contents of the directory inode * @dir_ino looking for the converted Unicode name. If the name is found in the * directory, the corresponding inode is loaded by calling ntfs_iget() on its * inode number and the inode is associated with the dentry @dent via a call to * d_splice_alias(). * * If the name is not found in the directory, a NULL inode is inserted into the * dentry @dent via a call to d_add(). The dentry is then termed a negative * dentry. * * Only if an actual error occurs, do we return an error via ERR_PTR(). * * In order to handle the case insensitivity issues of NTFS with regards to the * dcache and the dcache requiring only one dentry per directory, we deal with * dentry aliases that only differ in case in ->ntfs_lookup() while maintaining * a case sensitive dcache. This means that we get the full benefit of dcache * speed when the file/directory is looked up with the same case as returned by * ->ntfs_readdir() but that a lookup for any other case (or for the short file * name) will not find anything in dcache and will enter ->ntfs_lookup() * instead, where we search the directory for a fully matching file name * (including case) and if that is not found, we search for a file name that * matches with different case and if that has non-POSIX semantics we return * that. We actually do only one search (case sensitive) and keep tabs on * whether we have found a case insensitive match in the process. * * To simplify matters for us, we do not treat the short vs long filenames as * two hard links but instead if the lookup matches a short filename, we * return the dentry for the corresponding long filename instead. * * There are three cases we need to distinguish here: * * 1) @dent perfectly matches (i.e. including case) a directory entry with a * file name in the WIN32 or POSIX namespaces. In this case * ntfs_lookup_inode_by_name() will return with name set to NULL and we * just d_splice_alias() @dent. * 2) @dent matches (not including case) a directory entry with a file name in * the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return * with name set to point to a kmalloc()ed ntfs_name structure containing * the properly cased little endian Unicode name. We convert the name to the * current NLS code page, search if a dentry with this name already exists * and if so return that instead of @dent. At this point things are * complicated by the possibility of 'disconnected' dentries due to NFS * which we deal with appropriately (see the code comments). The VFS will * then destroy the old @dent and use the one we returned. If a dentry is * not found, we allocate a new one, d_splice_alias() it, and return it as * above. * 3) @dent matches either perfectly or not (i.e. we don't care about case) a * directory entry with a file name in the DOS namespace. In this case * ntfs_lookup_inode_by_name() will return with name set to point to a * kmalloc()ed ntfs_name structure containing the mft reference (cpu endian) * of the inode. We use the mft reference to read the inode and to find the * file name in the WIN32 namespace corresponding to the matched short file * name. We then convert the name to the current NLS code page, and proceed * searching for a dentry with this name, etc, as in case 2), above. * * Locking: Caller must hold i_mutex on the directory. */ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, struct nameidata *nd) { ntfs_volume *vol = NTFS_SB(dir_ino->i_sb); struct inode *dent_inode; ntfschar *uname; ntfs_name *name = NULL; MFT_REF mref; unsigned long dent_ino; int uname_len; ntfs_debug("Looking up %s in directory inode 0x%lx.", dent->d_name.name, dir_ino->i_ino); /* Convert the name of the dentry to Unicode. */ uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len, &uname); if (uname_len < 0) { if (uname_len != -ENAMETOOLONG) ntfs_error(vol->sb, "Failed to convert name to " "Unicode."); return ERR_PTR(uname_len); } mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len, &name); kmem_cache_free(ntfs_name_cache, uname); if (!IS_ERR_MREF(mref)) { dent_ino = MREF(mref); ntfs_debug("Found inode 0x%lx. Calling ntfs_iget.", dent_ino); dent_inode = ntfs_iget(vol->sb, dent_ino); if (likely(!IS_ERR(dent_inode))) { /* Consistency check. */ if (is_bad_inode(dent_inode) || MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || dent_ino == FILE_MFT) { /* Perfect WIN32/POSIX match. -- Case 1. */ if (!name) { ntfs_debug("Done. (Case 1.)"); return d_splice_alias(dent_inode, dent); } /* * We are too indented. Handle imperfect * matches and short file names further below. */ goto handle_name; } ntfs_error(vol->sb, "Found stale reference to inode " "0x%lx (reference sequence number = " "0x%x, inode sequence number = 0x%x), " "returning -EIO. Run chkdsk.", dent_ino, MSEQNO(mref), NTFS_I(dent_inode)->seq_no); iput(dent_inode); dent_inode = ERR_PTR(-EIO); } else ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with " "error code %li.", dent_ino, PTR_ERR(dent_inode)); kfree(name); /* Return the error code. */ return (struct dentry *)dent_inode; } /* It is guaranteed that @name is no longer allocated at this point. */ if (MREF_ERR(mref) == -ENOENT) { ntfs_debug("Entry was not found, adding negative dentry."); /* The dcache will handle negative entries. */ d_add(dent, NULL); ntfs_debug("Done."); return NULL; } ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error " "code %i.", -MREF_ERR(mref)); return ERR_PTR(MREF_ERR(mref)); // TODO: Consider moving this lot to a separate function! (AIA) handle_name: { struct dentry *real_dent, *new_dent; MFT_RECORD *m; ntfs_attr_search_ctx *ctx; ntfs_inode *ni = NTFS_I(dent_inode); int err; struct qstr nls_name; nls_name.name = NULL; if (name->type != FILE_NAME_DOS) { /* Case 2. */ ntfs_debug("Case 2."); nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&name->name, name->len, (unsigned char**)&nls_name.name, 0); kfree(name); } else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */ FILE_NAME_ATTR *fn; ntfs_debug("Case 3."); kfree(name); /* Find the WIN32 name corresponding to the matched DOS name. */ ni = NTFS_I(dent_inode); m = map_mft_record(ni); if (IS_ERR(m)) { err = PTR_ERR(m); m = NULL; ctx = NULL; goto err_out; } ctx = ntfs_attr_get_search_ctx(ni, m); if (unlikely(!ctx)) { err = -ENOMEM; goto err_out; } do { ATTR_RECORD *a; u32 val_len; err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_error(vol->sb, "Inode corrupt: No WIN32 " "namespace counterpart to DOS " "file name. Run chkdsk."); if (err == -ENOENT) err = -EIO; goto err_out; } /* Consistency checks. */ a = ctx->attr; if (a->non_resident || a->flags) goto eio_err_out; val_len = le32_to_cpu(a->data.resident.value_length); if (le16_to_cpu(a->data.resident.value_offset) + val_len > le32_to_cpu(a->length)) goto eio_err_out; fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu( ctx->attr->data.resident.value_offset)); if ((u32)(fn->file_name_length * sizeof(ntfschar) + sizeof(FILE_NAME_ATTR)) > val_len) goto eio_err_out; } while (fn->file_name_type != FILE_NAME_WIN32); /* Convert the found WIN32 name to current NLS code page. */ nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&fn->file_name, fn->file_name_length, (unsigned char**)&nls_name.name, 0); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); } m = NULL; ctx = NULL; /* Check if a conversion error occurred. */ if ((signed)nls_name.len < 0) { err = (signed)nls_name.len; goto err_out; } nls_name.hash = full_name_hash(nls_name.name, nls_name.len); /* * Note: No need for dent->d_lock lock as i_mutex is held on the * parent inode. */ /* Does a dentry matching the nls_name exist already? */ real_dent = d_lookup(dent->d_parent, &nls_name); /* If not, create it now. */ if (!real_dent) { real_dent = d_alloc(dent->d_parent, &nls_name); kfree(nls_name.name); if (!real_dent) { err = -ENOMEM; goto err_out; } new_dent = d_splice_alias(dent_inode, real_dent); if (new_dent) dput(real_dent); else new_dent = real_dent; ntfs_debug("Done. (Created new dentry.)"); return new_dent; } kfree(nls_name.name); /* Matching dentry exists, check if it is negative. */ if (real_dent->d_inode) { if (unlikely(real_dent->d_inode != dent_inode)) { /* This can happen because bad inodes are unhashed. */ BUG_ON(!is_bad_inode(dent_inode)); BUG_ON(!is_bad_inode(real_dent->d_inode)); } /* * Already have the inode and the dentry attached, decrement * the reference count to balance the ntfs_iget() we did * earlier on. We found the dentry using d_lookup() so it * cannot be disconnected and thus we do not need to worry * about any NFS/disconnectedness issues here. */ iput(dent_inode); ntfs_debug("Done. (Already had inode and dentry.)"); return real_dent; } /* * Negative dentry: instantiate it unless the inode is a directory and * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED), * in which case d_move() that in place of the found dentry. */ if (!S_ISDIR(dent_inode->i_mode)) { /* Not a directory; everything is easy. */ d_instantiate(real_dent, dent_inode); ntfs_debug("Done. (Already had negative file dentry.)"); return real_dent; } spin_lock(&dcache_lock); if (list_empty(&dent_inode->i_dentry)) { /* * Directory without a 'disconnected' dentry; we need to do * d_instantiate() by hand because it takes dcache_lock which * we already hold. */ list_add(&real_dent->d_alias, &dent_inode->i_dentry); real_dent->d_inode = dent_inode; spin_unlock(&dcache_lock); security_d_instantiate(real_dent, dent_inode); ntfs_debug("Done. (Already had negative directory dentry.)"); return real_dent; } /* * Directory with a 'disconnected' dentry; get a reference to the * 'disconnected' dentry. */ new_dent = list_entry(dent_inode->i_dentry.next, struct dentry, d_alias); dget_locked(new_dent); spin_unlock(&dcache_lock); /* Do security vodoo. */ security_d_instantiate(real_dent, dent_inode); /* Move new_dent in place of real_dent. */ d_move(new_dent, real_dent); /* Balance the ntfs_iget() we did above. */ iput(dent_inode); /* Throw away real_dent. */ dput(real_dent); /* Use new_dent as the actual dentry. */ ntfs_debug("Done. (Already had negative, disconnected directory " "dentry.)"); return new_dent; eio_err_out: ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk."); err = -EIO; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); if (m) unmap_mft_record(ni); iput(dent_inode); ntfs_error(vol->sb, "Failed, returning error code %i.", err); return ERR_PTR(err); } }
/** * ntfs_lookup - find the inode represented by a dentry in a directory inode * @dir_ino: directory inode in which to look for the inode * @dent: dentry representing the inode to look for * @flags: lookup flags * * In short, ntfs_lookup() looks for the inode represented by the dentry @dent * in the directory inode @dir_ino and if found attaches the inode to the * dentry @dent. * * In more detail, the dentry @dent specifies which inode to look for by * supplying the name of the inode in @dent->d_name.name. ntfs_lookup() * converts the name to Unicode and walks the contents of the directory inode * @dir_ino looking for the converted Unicode name. If the name is found in the * directory, the corresponding inode is loaded by calling ntfs_iget() on its * inode number and the inode is associated with the dentry @dent via a call to * d_splice_alias(). * * If the name is not found in the directory, a NULL inode is inserted into the * dentry @dent via a call to d_add(). The dentry is then termed a negative * dentry. * * Only if an actual error occurs, do we return an error via ERR_PTR(). * * In order to handle the case insensitivity issues of NTFS with regards to the * dcache and the dcache requiring only one dentry per directory, we deal with * dentry aliases that only differ in case in ->ntfs_lookup() while maintaining * a case sensitive dcache. This means that we get the full benefit of dcache * speed when the file/directory is looked up with the same case as returned by * ->ntfs_readdir() but that a lookup for any other case (or for the short file * name) will not find anything in dcache and will enter ->ntfs_lookup() * instead, where we search the directory for a fully matching file name * (including case) and if that is not found, we search for a file name that * matches with different case and if that has non-POSIX semantics we return * that. We actually do only one search (case sensitive) and keep tabs on * whether we have found a case insensitive match in the process. * * To simplify matters for us, we do not treat the short vs long filenames as * two hard links but instead if the lookup matches a short filename, we * return the dentry for the corresponding long filename instead. * * There are three cases we need to distinguish here: * * 1) @dent perfectly matches (i.e. including case) a directory entry with a * file name in the WIN32 or POSIX namespaces. In this case * ntfs_lookup_inode_by_name() will return with name set to NULL and we * just d_splice_alias() @dent. * 2) @dent matches (not including case) a directory entry with a file name in * the WIN32 namespace. In this case ntfs_lookup_inode_by_name() will return * with name set to point to a kmalloc()ed ntfs_name structure containing * the properly cased little endian Unicode name. We convert the name to the * current NLS code page, search if a dentry with this name already exists * and if so return that instead of @dent. At this point things are * complicated by the possibility of 'disconnected' dentries due to NFS * which we deal with appropriately (see the code comments). The VFS will * then destroy the old @dent and use the one we returned. If a dentry is * not found, we allocate a new one, d_splice_alias() it, and return it as * above. * 3) @dent matches either perfectly or not (i.e. we don't care about case) a * directory entry with a file name in the DOS namespace. In this case * ntfs_lookup_inode_by_name() will return with name set to point to a * kmalloc()ed ntfs_name structure containing the mft reference (cpu endian) * of the inode. We use the mft reference to read the inode and to find the * file name in the WIN32 namespace corresponding to the matched short file * name. We then convert the name to the current NLS code page, and proceed * searching for a dentry with this name, etc, as in case 2), above. * * Locking: Caller must hold i_mutex on the directory. */ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, unsigned int flags) { ntfs_volume *vol = NTFS_SB(dir_ino->i_sb); struct inode *dent_inode; ntfschar *uname; ntfs_name *name = NULL; MFT_REF mref; unsigned long dent_ino; int uname_len; ntfs_debug("Looking up %pd in directory inode 0x%lx.", dent, dir_ino->i_ino); /* Convert the name of the dentry to Unicode. */ uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len, &uname); if (uname_len < 0) { if (uname_len != -ENAMETOOLONG) ntfs_error(vol->sb, "Failed to convert name to " "Unicode."); return ERR_PTR(uname_len); } mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len, &name); kmem_cache_free(ntfs_name_cache, uname); if (!IS_ERR_MREF(mref)) { dent_ino = MREF(mref); ntfs_debug("Found inode 0x%lx. Calling ntfs_iget.", dent_ino); dent_inode = ntfs_iget(vol->sb, dent_ino); if (likely(!IS_ERR(dent_inode))) { /* Consistency check. */ if (is_bad_inode(dent_inode) || MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || dent_ino == FILE_MFT) { /* Perfect WIN32/POSIX match. -- Case 1. */ if (!name) { ntfs_debug("Done. (Case 1.)"); return d_splice_alias(dent_inode, dent); } /* * We are too indented. Handle imperfect * matches and short file names further below. */ goto handle_name; } ntfs_error(vol->sb, "Found stale reference to inode " "0x%lx (reference sequence number = " "0x%x, inode sequence number = 0x%x), " "returning -EIO. Run chkdsk.", dent_ino, MSEQNO(mref), NTFS_I(dent_inode)->seq_no); iput(dent_inode); dent_inode = ERR_PTR(-EIO); } else ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with " "error code %li.", dent_ino, PTR_ERR(dent_inode)); kfree(name); /* Return the error code. */ return ERR_CAST(dent_inode); } /* It is guaranteed that @name is no longer allocated at this point. */ if (MREF_ERR(mref) == -ENOENT) { ntfs_debug("Entry was not found, adding negative dentry."); /* The dcache will handle negative entries. */ d_add(dent, NULL); ntfs_debug("Done."); return NULL; } ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error " "code %i.", -MREF_ERR(mref)); return ERR_PTR(MREF_ERR(mref)); // TODO: Consider moving this lot to a separate function! (AIA) handle_name: { MFT_RECORD *m; ntfs_attr_search_ctx *ctx; ntfs_inode *ni = NTFS_I(dent_inode); int err; struct qstr nls_name; nls_name.name = NULL; if (name->type != FILE_NAME_DOS) { /* Case 2. */ ntfs_debug("Case 2."); nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&name->name, name->len, (unsigned char**)&nls_name.name, 0); kfree(name); } else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */ FILE_NAME_ATTR *fn; ntfs_debug("Case 3."); kfree(name); /* Find the WIN32 name corresponding to the matched DOS name. */ ni = NTFS_I(dent_inode); m = map_mft_record(ni); if (IS_ERR(m)) { err = PTR_ERR(m); m = NULL; ctx = NULL; goto err_out; } ctx = ntfs_attr_get_search_ctx(ni, m); if (unlikely(!ctx)) { err = -ENOMEM; goto err_out; } do { ATTR_RECORD *a; u32 val_len; err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_error(vol->sb, "Inode corrupt: No WIN32 " "namespace counterpart to DOS " "file name. Run chkdsk."); if (err == -ENOENT) err = -EIO; goto err_out; } /* Consistency checks. */ a = ctx->attr; if (a->non_resident || a->flags) goto eio_err_out; val_len = le32_to_cpu(a->data.resident.value_length); if (le16_to_cpu(a->data.resident.value_offset) + val_len > le32_to_cpu(a->length)) goto eio_err_out; fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu( ctx->attr->data.resident.value_offset)); if ((u32)(fn->file_name_length * sizeof(ntfschar) + sizeof(FILE_NAME_ATTR)) > val_len) goto eio_err_out; } while (fn->file_name_type != FILE_NAME_WIN32); /* Convert the found WIN32 name to current NLS code page. */ nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&fn->file_name, fn->file_name_length, (unsigned char**)&nls_name.name, 0); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); } m = NULL; ctx = NULL; /* Check if a conversion error occurred. */ if ((signed)nls_name.len < 0) { err = (signed)nls_name.len; goto err_out; } nls_name.hash = full_name_hash(dent, nls_name.name, nls_name.len); dent = d_add_ci(dent, dent_inode, &nls_name); kfree(nls_name.name); return dent; eio_err_out: ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk."); err = -EIO; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); if (m) unmap_mft_record(ni); iput(dent_inode); ntfs_error(vol->sb, "Failed, returning error code %i.", err); return ERR_PTR(err); } }
/** * change_label - change the current label on a device * @dev: device to change the label on * @mnt_flags: mount flags of the device or 0 if not mounted * @mnt_point: mount point of the device or NULL * @label: the new label * * Change the label on the device @dev to @label. */ static int change_label(ntfs_volume *vol, unsigned long mnt_flags, char *label, BOOL force) { ntfs_attr_search_ctx *ctx; ntfschar *new_label = NULL; ATTR_RECORD *a; int label_len; int result = 0; //XXX significant? if (mnt_flags & NTFS_MF_MOUNTED) { /* If not the root fs or mounted read/write, refuse change. */ if (!(mnt_flags & NTFS_MF_ISROOT) || !(mnt_flags & NTFS_MF_READONLY)) { if (!force) { ntfs_log_error("Refusing to change label on " "read-%s mounted device %s.\n", mnt_flags & NTFS_MF_READONLY ? "only" : "write", opts.device); return 1; } } } ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); if (!ctx) { ntfs_log_perror("Failed to get attribute search context"); goto err_out; } if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { if (errno != ENOENT) { ntfs_log_perror("Lookup of $VOLUME_NAME attribute failed"); goto err_out; } /* The volume name attribute does not exist. Need to add it. */ a = NULL; } else { a = ctx->attr; if (a->non_resident) { ntfs_log_error("Error: Attribute $VOLUME_NAME must be " "resident.\n"); goto err_out; } } label_len = ntfs_mbstoucs(label, &new_label, 0); if (label_len == -1) { ntfs_log_perror("Unable to convert label string to Unicode"); goto err_out; } label_len *= sizeof(ntfschar); if (label_len > 0x100) { ntfs_log_error("New label is too long. Maximum %u characters " "allowed. Truncating excess characters.\n", (unsigned)(0x100 / sizeof(ntfschar))); label_len = 0x100; new_label[label_len / sizeof(ntfschar)] = 0; } if (a) { if (resize_resident_attribute_value(ctx->mrec, a, label_len)) { ntfs_log_perror("Error resizing resident attribute"); goto err_out; } } else { /* sizeof(resident attribute record header) == 24 */ int asize = (24 + label_len + 7) & ~7; u32 biu = le32_to_cpu(ctx->mrec->bytes_in_use); if (biu + asize > le32_to_cpu(ctx->mrec->bytes_allocated)) { errno = ENOSPC; ntfs_log_perror("Error adding resident attribute"); goto err_out; } a = ctx->attr; memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)ctx->mrec)); ctx->mrec->bytes_in_use = cpu_to_le32(biu + asize); a->type = AT_VOLUME_NAME; a->length = cpu_to_le32(asize); a->non_resident = 0; a->name_length = 0; a->name_offset = cpu_to_le16(24); a->flags = cpu_to_le16(0); a->instance = ctx->mrec->next_attr_instance; ctx->mrec->next_attr_instance = cpu_to_le16((le16_to_cpu( ctx->mrec->next_attr_instance) + 1) & 0xffff); a->u.res.value_length = cpu_to_le32(label_len); a->u.res.value_offset = a->name_offset; a->u.res.resident_flags = 0; a->u.res.reservedR = 0; } memcpy((u8*)a + le16_to_cpu(a->u.res.value_offset), new_label, label_len); if (!opts.noaction && ntfs_inode_sync(vol->vol_ni)) { ntfs_log_perror("Error writing MFT Record to disk"); goto err_out; } result = 0; err_out: free(new_label); return result; }
/** * ntfs_inode_open - open an inode ready for access * @vol: volume to get the inode from * @mref: inode number / mft record number to open * * Allocate an ntfs_inode structure and initialize it for the given inode * specified by @mref. @mref specifies the inode number / mft record to read, * including the sequence number, which can be 0 if no sequence number checking * is to be performed. * * Then, allocate a buffer for the mft record, read the mft record from the * volume @vol, and attach it to the ntfs_inode structure (->mrec). The * mft record is mst deprotected and sanity checked for validity and we abort * if deprotection or checks fail. * * Finally, search for an attribute list attribute in the mft record and if one * is found, load the attribute list attribute value and attach it to the * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate * this. * * Return a pointer to the ntfs_inode structure on success or NULL on error, * with errno set to the error code. */ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) { s64 l; ntfs_inode *ni = NULL; ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; le32 lthle; int olderrno; ntfs_log_enter("Entering for inode %lld\n", (long long)MREF(mref)); if (!vol) { errno = EINVAL; goto out; } ni = __ntfs_inode_allocate(vol); if (!ni) goto out; if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL)) goto err_out; if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) { errno = ENOENT; goto err_out; } ni->mft_no = MREF(mref); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) goto err_out; /* Receive some basic information about inode. */ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { if (!ni->mrec->base_mft_record) ntfs_log_perror("No STANDARD_INFORMATION in base record" " %lld", (long long)MREF(mref)); goto put_err_out; } std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); ni->flags = std_info->file_attributes; ni->creation_time = ntfs2utc(std_info->creation_time); ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time); ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time); ni->last_access_time = ntfs2utc(std_info->last_access_time); /* JPA insert v3 extensions if present */ /* length may be seen as 72 (v1.x) or 96 (v3.x) */ lthle = ctx->attr->length; if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) { set_nino_flag(ni, v3_Extensions); ni->owner_id = std_info->owner_id; ni->security_id = std_info->security_id; ni->quota_charged = std_info->quota_charged; ni->usn = std_info->usn; } else { clear_nino_flag(ni, v3_Extensions); ni->owner_id = 0; ni->security_id = 0; } /* Set attribute list information. */ olderrno = errno; if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { if (errno != ENOENT) goto put_err_out; /* Attribute list attribute does not present. */ /* restore previous errno to avoid misinterpretation */ errno = olderrno; goto get_size; } NInoSetAttrList(ni); l = ntfs_get_attribute_value_length(ctx->attr); if (!l) goto put_err_out; if (l > 0x40000) { errno = EIO; ntfs_log_perror("Too large attrlist attribute (%lld), inode " "%lld", (long long)l, (long long)MREF(mref)); goto put_err_out; } ni->attr_list_size = l; ni->attr_list = ntfs_malloc(ni->attr_list_size); if (!ni->attr_list) goto put_err_out; l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list); if (!l) goto put_err_out; if (l != ni->attr_list_size) { errno = EIO; ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " "%lld", (long long)l, ni->attr_list_size, (long long)MREF(mref)); goto put_err_out; } get_size: olderrno = errno; if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { if (errno != ENOENT) goto put_err_out; /* Directory or special file. */ /* restore previous errno to avoid misinterpretation */ errno = olderrno; ni->data_size = ni->allocated_size = 0; } else { if (ctx->attr->non_resident) { ni->data_size = sle64_to_cpu(ctx->attr->data_size); if (ctx->attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ni->allocated_size = sle64_to_cpu( ctx->attr->compressed_size); else ni->allocated_size = sle64_to_cpu( ctx->attr->allocated_size); } else { ni->data_size = le32_to_cpu(ctx->attr->value_length); ni->allocated_size = (ni->data_size + 7) & ~7; } } ntfs_attr_put_search_ctx(ctx); out: ntfs_log_leave("\n"); return ni; put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: __ntfs_inode_release(ni); ni = NULL; goto out; }
/** * ntfs_inode_free_space - free space in the MFT record of an inode * @ni: ntfs inode in which MFT record needs more free space * @size: amount of space needed to free * * Return 0 on success or -1 on error with errno set to the error code. */ int ntfs_inode_free_space(ntfs_inode *ni, int size) { ntfs_attr_search_ctx *ctx; int freed; if (!ni || size < 0) { errno = EINVAL; ntfs_log_perror("%s: ni=%p size=%d", __FUNCTION__, ni, size); return -1; } ntfs_log_trace("Entering for inode %lld, size %d\n", (unsigned long long)ni->mft_no, size); freed = (le32_to_cpu(ni->mrec->bytes_allocated) - le32_to_cpu(ni->mrec->bytes_in_use)); if (size <= freed) return 0; ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) return -1; /* * $STANDARD_INFORMATION and $ATTRIBUTE_LIST must stay in the base MFT * record, so position search context on the first attribute after them. */ if (ntfs_attr_position(AT_FILE_NAME, ctx)) goto put_err_out; while (1) { int record_size; /* * Check whether attribute is from different MFT record. If so, * find next, because we don't need such. */ while (ctx->ntfs_ino->mft_no != ni->mft_no) { retry: if (ntfs_attr_position(AT_UNUSED, ctx)) goto put_err_out; } if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && ctx->attr->type == AT_DATA) goto retry; if (ctx->attr->type == AT_INDEX_ROOT) goto retry; record_size = le32_to_cpu(ctx->attr->length); if (ntfs_attr_record_move_away(ctx, 0)) { ntfs_log_perror("Failed to move out attribute #2"); break; } freed += record_size; /* Check whether we are done. */ if (size <= freed) { ntfs_attr_put_search_ctx(ctx); return 0; } /* * Reposition to first attribute after $STANDARD_INFORMATION * and $ATTRIBUTE_LIST instead of simply skipping this attribute * because in the case when we have got only in-memory attribute * list then ntfs_attr_lookup will fail when it tries to find * $ATTRIBUTE_LIST. */ ntfs_attr_reinit_search_ctx(ctx); if (ntfs_attr_position(AT_FILE_NAME, ctx)) break; } put_err_out: ntfs_attr_put_search_ctx(ctx); if (errno == ENOSPC) ntfs_log_trace("No attributes left that could be moved out.\n"); return -1; }
* provided they fit in requested size. * * Returns the modified size if successfull (or 32 if buffer size is null) * -errno if failed */ int ntfs_inode_get_times(const char *path __attribute__((unused)), char *value, size_t size, ntfs_inode *ni) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; u64 *times; int ret; ret = 0; ctx = ntfs_attr_get_search_ctx(ni, NULL); if (ctx) { if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Failed to get standard info (inode %lld)", (long long)ni->mft_no); } else { std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if (value && (size >= 8)) { times = (u64*)value; times[0] = le64_to_cpu(std_info->creation_time); ret = 8; if (size >= 16) { times[1] = le64_to_cpu(std_info->last_data_change_time); ret = 16;
/** * ntfs_inode_sync_file_name - update FILE_NAME attributes * @ni: ntfs inode to update FILE_NAME attributes * * Update all FILE_NAME attributes for inode @ni in the index. * * Return 0 on success or -1 on error with errno set to the error code. */ static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) { ntfs_attr_search_ctx *ctx = NULL; ntfs_index_context *ictx; ntfs_inode *index_ni; FILE_NAME_ATTR *fn; FILE_NAME_ATTR *fnx; REPARSE_POINT *rpp; le32 reparse_tag; int err = 0; ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; goto err_out; } /* Collect the reparse tag, if any */ reparse_tag = cpu_to_le32(0); if (ni->flags & FILE_ATTR_REPARSE_POINT) { if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { rpp = (REPARSE_POINT*)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); reparse_tag = rpp->reparse_tag; } ntfs_attr_reinit_search_ctx(ctx); } /* Walk through all FILE_NAME attributes and update them. */ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if (MREF_LE(fn->parent_directory) == ni->mft_no) { /* * WARNING: We cheat here and obtain 2 attribute * search contexts for one inode (first we obtained * above, second will be obtained inside * ntfs_index_lookup), it's acceptable for library, * but will deadlock in the kernel. */ index_ni = ni; } else if (dir_ni) index_ni = dir_ni; else index_ni = ntfs_inode_open(ni->vol, le64_to_cpu(fn->parent_directory)); if (!index_ni) { if (!err) err = errno; ntfs_log_perror("Failed to open inode %lld with index", (long long)le64_to_cpu(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); if (!ictx) { if (!err) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); if ((ni != index_ni) && !dir_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { if (!err) { if (errno == ENOENT) err = EIO; else err = errno; } ntfs_log_perror("Index lookup failed, inode %lld", (long long)index_ni->mft_no); ntfs_index_ctx_put(ictx); if (ni != index_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } /* Update flags and file size. */ fnx = (FILE_NAME_ATTR *)ictx->data; fnx->file_attributes = (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | (ni->flags & FILE_ATTR_VALID_FLAGS); if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) fnx->data_size = fnx->allocated_size = const_cpu_to_le64(0); else { fnx->allocated_size = cpu_to_sle64(ni->allocated_size); fnx->data_size = cpu_to_sle64(ni->data_size); /* * The file name record has also to be fixed if some * attribute update implied the unnamed data to be * made non-resident */ fn->allocated_size = fnx->allocated_size; } /* update or clear the reparse tag in the index */ fnx->reparse_point_tag = reparse_tag; if (!test_nino_flag(ni, TimesSet)) { fnx->creation_time = ni->creation_time; fnx->last_data_change_time = ni->last_data_change_time; fnx->last_mft_change_time = ni->last_mft_change_time; fnx->last_access_time = ni->last_access_time; } else { fnx->creation_time = fn->creation_time; fnx->last_data_change_time = fn->last_data_change_time; fnx->last_mft_change_time = fn->last_mft_change_time; fnx->last_access_time = fn->last_access_time; } ntfs_index_entry_mark_dirty(ictx); ntfs_index_ctx_put(ictx); if ((ni != index_ni) && !dir_ni && ntfs_inode_close(index_ni) && !err) err = errno; } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; ntfs_log_perror("Attribute lookup failed, inode %lld", (long long)ni->mft_no); goto err_out; } ntfs_attr_put_search_ctx(ctx); if (err) { errno = err; return -1; } return 0; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; return -1; }
int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, int flags) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; FILE_NAME_ATTR *fn; const u64 *times; ntfs_time now; int cnt; int ret; ret = -1; if ((size >= 8) && !(flags & XATTR_CREATE)) { times = (const u64*)value; now = ntfs_current_time(); /* update the standard information attribute */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (ctx) { if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_perror("Failed to get standard info (inode %lld)", (long long)ni->mft_no); } else { std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); /* * Mark times set to avoid overwriting * them when the inode is closed. * The inode structure must also be updated * (with loss of precision) because of cacheing. * TODO : use NTFS precision in inode, and * return sub-second times in getattr() */ set_nino_flag(ni, TimesSet); std_info->creation_time = cpu_to_le64(times[0]); ni->creation_time = std_info->creation_time; if (size >= 16) { std_info->last_data_change_time = cpu_to_le64(times[1]); ni->last_data_change_time = std_info->last_data_change_time; } if (size >= 24) { std_info->last_access_time = cpu_to_le64(times[2]); ni->last_access_time = std_info->last_access_time; } std_info->last_mft_change_time = now; ni->last_mft_change_time = now; ntfs_inode_mark_dirty(ctx->ntfs_ino); NInoFileNameSetDirty(ni); /* update the file names attributes */ ntfs_attr_reinit_search_ctx(ctx); cnt = 0; while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); fn->creation_time = cpu_to_le64(times[0]); if (size >= 16) fn->last_data_change_time = cpu_to_le64(times[1]); if (size >= 24) fn->last_access_time = cpu_to_le64(times[2]); fn->last_mft_change_time = now; cnt++; } if (cnt) ret = 0; else { ntfs_log_perror("Failed to get file names (inode %lld)", (long long)ni->mft_no); } } ntfs_attr_put_search_ctx(ctx); } } else if (size < 8) errno = ERANGE; else errno = EEXIST; return (ret); }
/** * info */ static int info(ntfs_volume *vol) { u64 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u; int cb, sb, cps; u64 uc = 0, mc = 0, fc = 0; struct mft_search_ctx *m_ctx; ntfs_attr_search_ctx *a_ctx; runlist_element *rl; ATTR_RECORD *rec; int z; int inuse = 0; m_ctx = mft_get_search_ctx(vol); m_ctx->flags_search = FEMR_IN_USE | FEMR_METADATA | FEMR_BASE_RECORD | FEMR_NOT_BASE_RECORD; while (mft_next_record(m_ctx) == 0) { if (!(m_ctx->flags_match & FEMR_IN_USE)) continue; inuse++; a_ctx = ntfs_attr_get_search_ctx(m_ctx->inode, NULL); while ((rec = find_attribute(AT_UNUSED, a_ctx))) { if (!rec->non_resident) continue; rl = ntfs_mapping_pairs_decompress(vol, rec, NULL); for (z = 0; rl[z].length > 0; z++) { if (rl[z].lcn >= 0) { if (m_ctx->flags_match & FEMR_METADATA) mc += rl[z].length; else uc += rl[z].length; } } free(rl); } ntfs_attr_put_search_ctx(a_ctx); } mft_put_search_ctx(m_ctx); cb = vol->cluster_size_bits; sb = vol->sector_size_bits; cps = cb - sb; fc = vol->nr_clusters-mc-uc; fc <<= cb; mc <<= cb; uc <<= cb; a = vol->sector_size; b = vol->cluster_size; c = 1 << cps; d = vol->nr_clusters << cb; e = vol->nr_clusters; f = vol->nr_clusters >> cps; g = vol->mft_na->initialized_size >> vol->mft_record_size_bits; h = inuse; i = h * 100 / g; j = fc; k = fc >> sb; l = fc >> cb; m = fc * 100 / b / e; n = uc; o = uc >> sb; p = uc >> cb; q = uc * 100 / b / e; r = mc; s = mc >> sb; t = mc >> cb; u = mc * 100 / b / e; ntfs_log_info("bytes per sector : %llu\n", (unsigned long long)a); ntfs_log_info("bytes per cluster : %llu\n", (unsigned long long)b); ntfs_log_info("sectors per cluster : %llu\n", (unsigned long long)c); ntfs_log_info("bytes per volume : %llu\n", (unsigned long long)d); ntfs_log_info("sectors per volume : %llu\n", (unsigned long long)e); ntfs_log_info("clusters per volume : %llu\n", (unsigned long long)f); ntfs_log_info("initialized mft records : %llu\n", (unsigned long long)g); ntfs_log_info("mft records in use : %llu\n", (unsigned long long)h); ntfs_log_info("mft records percentage : %llu\n", (unsigned long long)i); ntfs_log_info("bytes of free space : %llu\n", (unsigned long long)j); ntfs_log_info("sectors of free space : %llu\n", (unsigned long long)k); ntfs_log_info("clusters of free space : %llu\n", (unsigned long long)l); ntfs_log_info("percentage free space : %llu\n", (unsigned long long)m); ntfs_log_info("bytes of user data : %llu\n", (unsigned long long)n); ntfs_log_info("sectors of user data : %llu\n", (unsigned long long)o); ntfs_log_info("clusters of user data : %llu\n", (unsigned long long)p); ntfs_log_info("percentage user data : %llu\n", (unsigned long long)q); ntfs_log_info("bytes of metadata : %llu\n", (unsigned long long)r); ntfs_log_info("sectors of metadata : %llu\n", (unsigned long long)s); ntfs_log_info("clusters of metadata : %llu\n", (unsigned long long)t); ntfs_log_info("percentage metadata : %llu\n", (unsigned long long)u); return 0; }
/** * ntfs_inode_sync_file_name - update FILE_NAME attributes * @ni: ntfs inode to update FILE_NAME attributes * * Update all FILE_NAME attributes for inode @ni in the index. * * Return 0 on success or -1 on error with errno set to the error code. */ static int ntfs_inode_sync_file_name(ntfs_inode *ni) { ntfs_attr_search_ctx *ctx = NULL; ntfs_index_context *ictx; ntfs_inode *index_ni; FILE_NAME_ATTR *fn; int err = 0; ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; goto err_out; } /* Walk through all FILE_NAME attributes and update them. */ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); if (MREF_LE(fn->parent_directory) == ni->mft_no) { /* * WARNING: We cheat here and obtain 2 attribute * search contexts for one inode (first we obtained * above, second will be obtained inside * ntfs_index_lookup), it's acceptable for library, * but will deadlock in the kernel. */ index_ni = ni; } else index_ni = ntfs_inode_open(ni->vol, le64_to_cpu(fn->parent_directory)); if (!index_ni) { if (!err) err = errno; ntfs_log_perror("Failed to open inode %lld with index", (long long)le64_to_cpu(fn->parent_directory)); continue; } ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4); if (!ictx) { if (!err) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); if (ni != index_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) { if (!err) { if (errno == ENOENT) err = EIO; else err = errno; } ntfs_log_perror("Index lookup failed, inode %lld", (long long)index_ni->mft_no); ntfs_index_ctx_put(ictx); if (ni != index_ni && ntfs_inode_close(index_ni) && !err) err = errno; continue; } /* Update flags and file size. */ fn = (FILE_NAME_ATTR *)ictx->data; fn->file_attributes = (fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) | (ni->flags & FILE_ATTR_VALID_FLAGS); fn->allocated_size = cpu_to_sle64(ni->allocated_size); fn->data_size = cpu_to_sle64(ni->data_size); if (test_nino_flag(ni, TimesDirty)) { 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); } ntfs_index_entry_mark_dirty(ictx); ntfs_index_ctx_put(ictx); if ((ni != index_ni) && ntfs_inode_close(index_ni) && !err) err = errno; } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; ntfs_log_perror("Attribute lookup failed, inode %lld", (long long)ni->mft_no); goto err_out; } ntfs_attr_put_search_ctx(ctx); if (err) { errno = err; return -1; } return 0; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); errno = err; return -1; }
/** * ntfs_inode_add_attrlist - add attribute list to inode and fill it * @ni: opened ntfs inode to which add attribute list * * Return 0 on success or -1 on error with errno set to the error code. * The following error codes are defined: * EINVAL - Invalid arguments were passed to the function. * EEXIST - Attribute list already exist. * EIO - Input/Ouput error occurred. * ENOMEM - Not enough memory to perform add. */ int ntfs_inode_add_attrlist(ntfs_inode *ni) { int err; ntfs_attr_search_ctx *ctx; u8 *al = NULL, *aln; int al_len = 0; ATTR_LIST_ENTRY *ale = NULL; ntfs_attr *na; if (!ni) { errno = EINVAL; ntfs_log_perror("%s", __FUNCTION__); return -1; } ntfs_log_trace("inode %llu\n", (unsigned long long) ni->mft_no); if (NInoAttrList(ni) || ni->nr_extents) { errno = EEXIST; ntfs_log_perror("Inode already has attribute list"); return -1; } /* Form attribute list. */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; goto err_out; } /* Walk through all attributes. */ while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { int ale_size; if (ctx->attr->type == AT_ATTRIBUTE_LIST) { err = EIO; ntfs_log_perror("Attribute list already present"); goto put_err_out; } ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7; al_len += ale_size; aln = realloc(al, al_len); if (!aln) { err = errno; ntfs_log_perror("Failed to realloc %d bytes", al_len); goto put_err_out; } ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); al = aln; memset(ale, 0, ale_size); /* Add attribute to attribute list. */ ale->type = ctx->attr->type; ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7); ale->name_length = ctx->attr->name_length; ale->name_offset = (u8 *)ale->name - (u8 *)ale; if (ctx->attr->non_resident) ale->lowest_vcn = ctx->attr->lowest_vcn; else ale->lowest_vcn = 0; ale->mft_reference = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); ale->instance = ctx->attr->instance; memcpy(ale->name, (u8 *)ctx->attr + le16_to_cpu(ctx->attr->name_offset), ctx->attr->name_length * sizeof(ntfschar)); ale = (ATTR_LIST_ENTRY *)(al + al_len); } /* Check for real error occurred. */ if (errno != ENOENT) { err = errno; ntfs_log_perror("%s: Attribute lookup failed, inode %lld", __FUNCTION__, (long long)ni->mft_no); goto put_err_out; } /* Set in-memory attribute list. */ ni->attr_list = al; ni->attr_list_size = al_len; NInoSetAttrList(ni); NInoAttrListSetDirty(ni); /* Free space if there is not enough it for $ATTRIBUTE_LIST. */ if (le32_to_cpu(ni->mrec->bytes_allocated) - le32_to_cpu(ni->mrec->bytes_in_use) < offsetof(ATTR_RECORD, resident_end)) { if (ntfs_inode_free_space(ni, offsetof(ATTR_RECORD, resident_end))) { /* Failed to free space. */ err = errno; ntfs_log_perror("Failed to free space for attrlist"); goto rollback; } } /* Add $ATTRIBUTE_LIST to mft record. */ if (ntfs_resident_attr_record_add(ni, AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) { err = errno; ntfs_log_perror("Couldn't add $ATTRIBUTE_LIST to MFT"); goto rollback; } /* Resize it. */ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_perror("Failed to open just added $ATTRIBUTE_LIST"); goto remove_attrlist_record; } if (ntfs_attr_truncate(na, al_len)) { err = errno; ntfs_log_perror("Failed to resize just added $ATTRIBUTE_LIST"); ntfs_attr_close(na); goto remove_attrlist_record;; } ntfs_attr_put_search_ctx(ctx); ntfs_attr_close(na); return 0; remove_attrlist_record: /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */ ni->attr_list = NULL; NInoClearAttrList(ni); /* Remove $ATTRIBUTE_LIST record. */ ntfs_attr_reinit_search_ctx(ctx); if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { if (ntfs_attr_record_rm(ctx)) ntfs_log_perror("Rollback failed to remove attrlist"); } else ntfs_log_perror("Rollback failed to find attrlist"); /* Setup back in-memory runlist. */ ni->attr_list = al; ni->attr_list_size = al_len; NInoSetAttrList(ni); rollback: /* * Scan attribute list for attributes that placed not in the base MFT * record and move them to it. */ ntfs_attr_reinit_search_ctx(ctx); ale = (ATTR_LIST_ENTRY*)al; while ((u8*)ale < al + al_len) { if (MREF_LE(ale->mft_reference) != ni->mft_no) { if (!ntfs_attr_lookup(ale->type, ale->name, ale->name_length, CASE_SENSITIVE, sle64_to_cpu(ale->lowest_vcn), NULL, 0, ctx)) { if (ntfs_attr_record_move_to(ctx, ni)) ntfs_log_perror("Rollback failed to " "move attribute"); } else ntfs_log_perror("Rollback failed to find attr"); ntfs_attr_reinit_search_ctx(ctx); } ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length)); } /* Remove in-memory attribute list. */ ni->attr_list = NULL; ni->attr_list_size = 0; NInoClearAttrList(ni); NInoAttrListClearDirty(ni); put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: free(al); errno = err; return -1; }
static int ntfs_fallocate(ntfs_inode *ni, s64 alloc_offs, s64 alloc_len) { s64 allocated_size; s64 data_size; ntfs_attr_search_ctx *ctx; ntfs_attr *na; runlist_element *oldrl; const char *errmess; int save_errno; int err; err = 0; /* Open the specified attribute. */ na = ntfs_attr_open(ni, attr_type, attr_name, attr_name_len); if (!na) { ntfs_log_perror("Failed to open attribute 0x%lx: ", (unsigned long)le32_to_cpu(attr_type)); err = -1; } else { errmess = (const char*)NULL; if (na->data_flags & ATTR_IS_COMPRESSED) { errmess= "Cannot fallocate a compressed file"; } /* Locate the attribute record, needed for updating sizes */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { errmess = "Failed to allocate a search context"; } if (errmess) { ntfs_log_error("%s\n",errmess); err = -1; } else { /* Get and save the initial allocations */ allocated_size = na->allocated_size; data_size = ni->data_size; err = ntfs_attr_map_whole_runlist(na); if (!err) { oldrl = ntfs_save_rl(na->rl); if (oldrl) { err = ntfs_full_allocation(na, ctx, alloc_offs, alloc_len); if (err) { save_errno = errno; ni->allocated_size = allocated_size; ni->data_size = data_size; ntfs_restore_rl(na, oldrl); errno = save_errno; } else { free(oldrl); /* Mark file name dirty, to update the sizes in directories */ NInoFileNameSetDirty(ni); NInoSetDirty(ni); } } else err = -1; } ntfs_attr_put_search_ctx(ctx); } /* Close the attribute. */ ntfs_attr_close(na); } return (err); }
/** * ntfs_attrlist_entry_add - add an attribute list attribute entry * @ni: opened ntfs inode, which contains that attribute * @attr: attribute record to add to attribute list * * Return 0 on success and -1 on error with errno set to the error code. The * following error codes are defined: * EINVAL - Invalid arguments passed to function. * ENOMEM - Not enough memory to allocate necessary buffers. * EIO - I/O error occurred or damaged filesystem. * EEXIST - Such attribute already present in attribute list. */ int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr) { ATTR_LIST_ENTRY *ale; leMFT_REF mref; ntfs_attr *na = NULL; ntfs_attr_search_ctx *ctx; u8 *new_al; int entry_len, entry_offset, err; ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (long long) ni->mft_no, (unsigned) le32_to_cpu(attr->type)); if (!ni || !attr) { ntfs_log_trace("Invalid arguments.\n"); errno = EINVAL; return -1; } mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); if (ni->nr_extents == -1) ni = ni->u.base_ni; if (!NInoAttrList(ni)) { ntfs_log_trace("Attribute list isn't present.\n"); errno = ENOENT; return -1; } /* Determine size and allocate memory for new attribute list. */ entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * attr->name_length + 7) & ~7; new_al = malloc(ni->attr_list_size + entry_len); if (!new_al) { ntfs_log_trace("Not enough memory.\n"); err = ENOMEM; return -1; } /* Find place for the new entry. */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (!ctx) { err = errno; ntfs_log_trace("Failed to obtain attribute search context.\n"); goto err_out; } if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*) ((u8*)attr + le16_to_cpu(attr->name_offset)) : AT_UNNAMED, attr->name_length, CASE_SENSITIVE, (attr->non_resident) ? sle64_to_cpu(attr->u.nonres.lowest_vcn) : 0, (attr->non_resident) ? NULL : ((u8*)attr + le16_to_cpu(attr->u.res.value_offset)), (attr->non_resident) ? 0 : le32_to_cpu(attr->u.res.value_length), ctx)) { /* Found some extent, check it to be before new extent. */ if (ctx->al_entry->lowest_vcn == attr->u.nonres.lowest_vcn) { err = EEXIST; ntfs_log_trace("Such attribute already present in the " "attribute list.\n"); ntfs_attr_put_search_ctx(ctx); goto err_out; } /* Add new entry after this extent. */ ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry + le16_to_cpu(ctx->al_entry->length)); } else { /* Check for real errors. */ if (errno != ENOENT) { err = errno; ntfs_log_trace("Attribute lookup failed.\n"); ntfs_attr_put_search_ctx(ctx); goto err_out; } /* No previous extents found. */ ale = ctx->al_entry; } /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ ntfs_attr_put_search_ctx(ctx); /* Determine new entry offset. */ entry_offset = ((u8 *)ale - ni->attr_list); /* Set pointer to new entry. */ ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset); /* Form new entry. */ ale->type = attr->type; ale->length = cpu_to_le16(entry_len); ale->name_length = attr->name_length; ale->name_offset = offsetof(ATTR_LIST_ENTRY, name); if (attr->non_resident) ale->lowest_vcn = attr->u.nonres.lowest_vcn; else ale->lowest_vcn = 0; ale->mft_reference = mref; ale->instance = attr->instance; NTFS_ON_DEBUG(memset(ale->name, 0, ((u8*)((u8*)ale + entry_len)) - ((u8*)ale->name))); /* Shut up, valgrind. */ memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), attr->name_length * sizeof(ntfschar)); /* Resize $ATTRIBUTE_LIST to new length. */ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); if (!na) { err = errno; ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n"); goto err_out; } if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) { err = errno; ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n"); goto err_out; } /* Copy entries from old attribute list to new. */ memcpy(new_al, ni->attr_list, entry_offset); memcpy(new_al + entry_offset + entry_len, ni->attr_list + entry_offset, ni->attr_list_size - entry_offset); /* Set new runlist. */ free(ni->attr_list); ni->attr_list = new_al; ni->attr_list_size = ni->attr_list_size + entry_len; NInoAttrListSetDirty(ni); /* Done! */ ntfs_attr_close(na); return 0; err_out: if (na) ntfs_attr_close(na); free(new_al); errno = err; return -1; }
/** * ntfs_td_list_entry * FIXME: Should we print errors as we go along? (AIA) */ static int ntfs_td_list_entry( struct ntfs_dir_struct *ls, const ntfschar *name, const int name_len, const int name_type, const s64 pos, const MFT_REF mref, const unsigned dt_type) { int result = 0; char *filename; ntfs_inode *ni; ntfs_attr_search_ctx *ctx_si = NULL; file_info_t *new_file=NULL; /* Keep FILE_NAME_WIN32 and FILE_NAME_POSIX */ if ((name_type & FILE_NAME_WIN32_AND_DOS) == FILE_NAME_DOS) return 0; filename = (char *)calloc (1, MAX_PATH); if (!filename) { log_critical("ntfs_td_list_entry calloc failed\n"); return -1; } #ifdef HAVE_ICONV if (ntfs_ucstoutf8(ls->cd, name, name_len, &filename, MAX_PATH) < 0 && ntfs_ucstombs (name, name_len, &filename, MAX_PATH) < 0) { log_error("Cannot represent filename in current locale.\n"); goto freefn; } #else if (ntfs_ucstombs (name, name_len, &filename, MAX_PATH) < 0) { log_error("Cannot represent filename in current locale.\n"); goto freefn; } #endif result = 0; /* These are successful */ if ((ls->dir_data->param & FLAG_LIST_SYSTEM)!=FLAG_LIST_SYSTEM && MREF(mref) < FILE_first_user && filename[0] == '$') /* Hide system file */ goto freefn; result = -1; /* Everything else is bad */ ni = ntfs_inode_open(ls->vol, mref); if (!ni) goto freefn; new_file=(file_info_t*)MALLOC(sizeof(*new_file)); new_file->status=0; new_file->st_ino=MREF(mref); new_file->st_uid=0; new_file->st_gid=0; ctx_si = ntfs_attr_get_search_ctx(ni, ni->mrec); if (ctx_si) { if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED, 0, CASE_SENSITIVE, 0, NULL, 0, ctx_si)==0) { const ATTR_RECORD *attr = ctx_si->attr; const STANDARD_INFORMATION *si = (const STANDARD_INFORMATION*)((const char*)attr + le16_to_cpu(attr->value_offset)); if(si) { new_file->td_atime=td_ntfs2utc(sle64_to_cpu(si->last_access_time)); new_file->td_mtime=td_ntfs2utc(sle64_to_cpu(si->last_data_change_time)); new_file->td_ctime=td_ntfs2utc(sle64_to_cpu(si->creation_time)); } } ntfs_attr_put_search_ctx(ctx_si); } { ATTR_RECORD *rec; int first=1; ntfs_attr_search_ctx *ctx = NULL; if (dt_type == NTFS_DT_DIR) { new_file->name=strdup(filename); new_file->st_mode = LINUX_S_IFDIR| LINUX_S_IRUGO | LINUX_S_IXUGO; new_file->st_size=0; td_list_add_tail(&new_file->list, &ls->dir_list->list); first=0; } ctx = ntfs_attr_get_search_ctx(ni, ni->mrec); /* A file has always an unnamed date stream and * may have named alternate data streams (ADS) */ while((rec = find_attribute(AT_DATA, ctx))) { const s64 filesize = ntfs_get_attribute_value_length(ctx->attr); if(rec->name_length && (ls->dir_data->param & FLAG_LIST_ADS)!=FLAG_LIST_ADS) continue; if(first==0) { const file_info_t *old_file=new_file; new_file=(file_info_t *)MALLOC(sizeof(*new_file)); memcpy(new_file, old_file, sizeof(*new_file)); } new_file->st_mode = LINUX_S_IFREG | LINUX_S_IRUGO; new_file->st_size=filesize; if (rec->name_length) { char *stream_name=NULL; new_file->status=FILE_STATUS_ADS; new_file->name = (char *)MALLOC(MAX_PATH); if (ntfs_ucstombs((ntfschar *) ((char *) rec + le16_to_cpu(rec->name_offset)), rec->name_length, &stream_name, 0) < 0) { log_error("ERROR: Cannot translate name into current locale.\n"); snprintf(new_file->name, MAX_PATH, "%s:???", filename); } else { snprintf(new_file->name, MAX_PATH, "%s:%s", filename, stream_name); } free(stream_name); } else { new_file->name=strdup(filename); } td_list_add_tail(&new_file->list, &ls->dir_list->list); first=0; } ntfs_attr_put_search_ctx(ctx); if(first) { free(new_file); } } result = 0; /* close the inode. */ ntfs_inode_close(ni); freefn: free (filename); return result; }
static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent, struct nameidata *nd) { ntfs_volume *vol = NTFS_SB(dir_ino->i_sb); struct inode *dent_inode; ntfschar *uname; ntfs_name *name = NULL; MFT_REF mref; unsigned long dent_ino; int uname_len; ntfs_debug("Looking up %s in directory inode 0x%lx.", dent->d_name.name, dir_ino->i_ino); uname_len = ntfs_nlstoucs(vol, dent->d_name.name, dent->d_name.len, &uname); if (uname_len < 0) { if (uname_len != -ENAMETOOLONG) ntfs_error(vol->sb, "Failed to convert name to " "Unicode."); return ERR_PTR(uname_len); } mref = ntfs_lookup_inode_by_name(NTFS_I(dir_ino), uname, uname_len, &name); kmem_cache_free(ntfs_name_cache, uname); if (!IS_ERR_MREF(mref)) { dent_ino = MREF(mref); ntfs_debug("Found inode 0x%lx. Calling ntfs_iget.", dent_ino); dent_inode = ntfs_iget(vol->sb, dent_ino); if (likely(!IS_ERR(dent_inode))) { if (is_bad_inode(dent_inode) || MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || dent_ino == FILE_MFT) { if (!name) { ntfs_debug("Done. (Case 1.)"); return d_splice_alias(dent_inode, dent); } goto handle_name; } ntfs_error(vol->sb, "Found stale reference to inode " "0x%lx (reference sequence number = " "0x%x, inode sequence number = 0x%x), " "returning -EIO. Run chkdsk.", dent_ino, MSEQNO(mref), NTFS_I(dent_inode)->seq_no); iput(dent_inode); dent_inode = ERR_PTR(-EIO); } else ntfs_error(vol->sb, "ntfs_iget(0x%lx) failed with " "error code %li.", dent_ino, PTR_ERR(dent_inode)); kfree(name); return (struct dentry *)dent_inode; } if (MREF_ERR(mref) == -ENOENT) { ntfs_debug("Entry was not found, adding negative dentry."); d_add(dent, NULL); ntfs_debug("Done."); return NULL; } ntfs_error(vol->sb, "ntfs_lookup_ino_by_name() failed with error " "code %i.", -MREF_ERR(mref)); return ERR_PTR(MREF_ERR(mref)); handle_name: { MFT_RECORD *m; ntfs_attr_search_ctx *ctx; ntfs_inode *ni = NTFS_I(dent_inode); int err; struct qstr nls_name; nls_name.name = NULL; if (name->type != FILE_NAME_DOS) { ntfs_debug("Case 2."); nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&name->name, name->len, (unsigned char**)&nls_name.name, 0); kfree(name); } else { FILE_NAME_ATTR *fn; ntfs_debug("Case 3."); kfree(name); ni = NTFS_I(dent_inode); m = map_mft_record(ni); if (IS_ERR(m)) { err = PTR_ERR(m); m = NULL; ctx = NULL; goto err_out; } ctx = ntfs_attr_get_search_ctx(ni, m); if (unlikely(!ctx)) { err = -ENOMEM; goto err_out; } do { ATTR_RECORD *a; u32 val_len; err = ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx); if (unlikely(err)) { ntfs_error(vol->sb, "Inode corrupt: No WIN32 " "namespace counterpart to DOS " "file name. Run chkdsk."); if (err == -ENOENT) err = -EIO; goto err_out; } a = ctx->attr; if (a->non_resident || a->flags) goto eio_err_out; val_len = le32_to_cpu(a->data.resident.value_length); if (le16_to_cpu(a->data.resident.value_offset) + val_len > le32_to_cpu(a->length)) goto eio_err_out; fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + le16_to_cpu( ctx->attr->data.resident.value_offset)); if ((u32)(fn->file_name_length * sizeof(ntfschar) + sizeof(FILE_NAME_ATTR)) > val_len) goto eio_err_out; } while (fn->file_name_type != FILE_NAME_WIN32); nls_name.len = (unsigned)ntfs_ucstonls(vol, (ntfschar*)&fn->file_name, fn->file_name_length, (unsigned char**)&nls_name.name, 0); ntfs_attr_put_search_ctx(ctx); unmap_mft_record(ni); } m = NULL; ctx = NULL; if ((signed)nls_name.len < 0) { err = (signed)nls_name.len; goto err_out; } nls_name.hash = full_name_hash(nls_name.name, nls_name.len); dent = d_add_ci(dent, dent_inode, &nls_name); kfree(nls_name.name); return dent; eio_err_out: ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk."); err = -EIO; err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); if (m) unmap_mft_record(ni); iput(dent_inode); ntfs_error(vol->sb, "Failed, returning error code %i.", err); return ERR_PTR(err); } }
int ntfs_change_label(ntfs_volume *vol, char *label) { ntfs_attr_search_ctx *ctx; ntfschar *new_label = NULL; ATTR_RECORD *a; int label_len; int result = 0; ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); if (!ctx) { ntfs_log_perror("Failed to get attribute search context"); goto err_out; } if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { if (errno != ENOENT) { ntfs_log_perror("Lookup of $VOLUME_NAME attribute failed"); goto err_out; } /* The volume name attribute does not exist. Need to add it. */ a = NULL; } else { a = ctx->attr; if (a->non_resident) { ntfs_log_error("Error: Attribute $VOLUME_NAME must be " "resident.\n"); goto err_out; } } label_len = ntfs_mbstoucs(label, &new_label); if (label_len == -1) { ntfs_log_perror("Unable to convert label string to Unicode"); goto err_out; } label_len *= sizeof(ntfschar); if (label_len > 0x100) { ntfs_log_error("New label is too long. Maximum %u characters " "allowed. Truncating excess characters.\n", (unsigned)(0x100 / sizeof(ntfschar))); label_len = 0x100; new_label[label_len / sizeof(ntfschar)] = cpu_to_le16(L'\0'); } if (a) { if (resize_resident_attribute_value(ctx->mrec, a, label_len)) { ntfs_log_perror("Error resizing resident attribute"); goto err_out; } } else { /* sizeof(resident attribute record header) == 24 */ int asize = (24 + label_len + 7) & ~7; u32 biu = le32_to_cpu(ctx->mrec->bytes_in_use); if (biu + asize > le32_to_cpu(ctx->mrec->bytes_allocated)) { errno = ENOSPC; ntfs_log_perror("Error adding resident attribute"); goto err_out; } a = ctx->attr; memmove((u8*)a + asize, a, biu - ((u8*)a - (u8*)ctx->mrec)); ctx->mrec->bytes_in_use = cpu_to_le32(biu + asize); a->type = AT_VOLUME_NAME; a->length = cpu_to_le32(asize); a->non_resident = 0; a->name_length = 0; a->name_offset = cpu_to_le16(24); a->flags = cpu_to_le16(0); a->instance = ctx->mrec->next_attr_instance; ctx->mrec->next_attr_instance = cpu_to_le16((le16_to_cpu( ctx->mrec->next_attr_instance) + 1) & 0xffff); a->value_length = cpu_to_le32(label_len); a->value_offset = a->name_offset; a->resident_flags = 0; a->reservedR = 0; } memcpy((u8*)a + le16_to_cpu(a->value_offset), new_label, label_len); if (ntfs_inode_sync(vol->vol_ni)) { ntfs_log_perror("Error writing MFT Record to disk"); goto err_out; } result = 0; err_out: ntfs_attr_put_search_ctx(ctx); free(new_label); return result; }