/** * ntfs_file_fsync - sync a file to disk * @filp: file to be synced * @dentry: dentry describing the file to sync * @datasync: if non-zero only flush user data and not metadata * * Data integrity sync of a file to disk. Used for fsync, fdatasync, and msync * system calls. This function is inspired by fs/buffer.c::file_fsync(). * * If @datasync is false, write the mft record and all associated extent mft * records as well as the $DATA attribute and then sync the block device. * * If @datasync is true and the attribute is non-resident, we skip the writing * of the mft record and all associated extent mft records (this might still * happen due to the write_inode_now() call). * * Also, if @datasync is true, we do not wait on the inode to be written out * but we always wait on the page cache pages to be written out. * * Note: In the past @filp could be NULL so we ignore it as we don't need it * anyway. * * Locking: Caller must hold i_sem on the inode. * * TODO: We should probably also write all attribute/index inodes associated * with this inode but since we have no simple way of getting to them we ignore * this problem for now. */ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode *vi = dentry->d_inode; int err, ret = 0; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); BUG_ON(S_ISDIR(vi->i_mode)); if (!datasync || !NInoNonResident(NTFS_I(vi))) ret = ntfs_write_inode(vi, 1); write_inode_now(vi, !datasync); /* * NOTE: If we were to use mapping->private_list (see ext2 and * fs/buffer.c) for dirty blocks then we could optimize the below to be * sync_mapping_buffers(vi->i_mapping). */ err = sync_blockdev(vi->i_sb->s_bdev); if (unlikely(err && !ret)) ret = err; if (likely(!ret)) ntfs_debug("Done."); else ntfs_warning(vi->i_sb, "Failed to f%ssync inode 0x%lx. Error " "%u.", datasync ? "data" : "", vi->i_ino, -ret); return ret; }
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_file_fsync - sync a file to disk * @filp: file to be synced * @dentry: dentry describing the file to sync * @datasync: if non-zero only flush user data and not metadata * * Data integrity sync of a file to disk. Used for fsync, fdatasync, and msync * system calls. This function is inspired by fs/buffer.c::file_fsync(). * * If @datasync is false, write the mft record and all associated extent mft * records as well as the $DATA attribute and then sync the block device. * * If @datasync is true and the attribute is non-resident, we skip the writing * of the mft record and all associated extent mft records (this might still * happen due to the write_inode_now() call). * * Also, if @datasync is true, we do not wait on the inode to be written out * but we always wait on the page cache pages to be written out. * * Note: In the past @filp could be NULL so we ignore it as we don't need it * anyway. * * Locking: Caller must hold i_sem on the inode. * * TODO: We should probably also write all attribute/index inodes associated * with this inode but since we have no simple way of getting to them we ignore * this problem for now. */ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode *vi = dentry->d_inode; int err, ret = 0; ntfs_debug("Entering for inode 0x%lx.", vi->i_ino); BUG_ON(S_ISDIR(vi->i_mode)); if (!datasync || !NInoNonResident(NTFS_I(vi))) ret = ntfs_write_inode(vi, 1); write_inode_now(vi, !datasync); err = sync_blockdev(vi->i_sb->s_bdev); if (unlikely(err && !ret)) ret = err; if (likely(!ret)) ntfs_debug("Done."); else ntfs_warning(vi->i_sb, "Failed to f%ssync inode 0x%lx. Error " "%u.", datasync ? "data" : "", vi->i_ino, -ret); return ret; }
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); } }
/** * 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); } }
/** * ntfs_mark_quotas_out_of_date - mark the quotas out of date on an ntfs volume * @vol: ntfs volume on which to mark the quotas out of date * * Mark the quotas out of date on the ntfs volume @vol and return TRUE on * success and FALSE on error. */ BOOL ntfs_mark_quotas_out_of_date(ntfs_volume *vol) { ntfs_index_context *ictx; QUOTA_CONTROL_ENTRY *qce; const le32 qid = QUOTA_DEFAULTS_ID; int err; ntfs_debug("Entering."); if (NVolQuotaOutOfDate(vol)) goto done; if (!vol->quota_ino || !vol->quota_q_ino) { ntfs_error(vol->sb, "Quota inodes are not open."); return FALSE; } down(&vol->quota_q_ino->i_sem); ictx = ntfs_index_ctx_get(NTFS_I(vol->quota_q_ino)); if (!ictx) { ntfs_error(vol->sb, "Failed to get index context."); goto err_out; } err = ntfs_index_lookup(&qid, sizeof(qid), ictx); if (err) { if (err == -ENOENT) ntfs_error(vol->sb, "Quota defaults entry is not " "present."); else ntfs_error(vol->sb, "Lookup of quota defaults entry " "failed."); goto err_out; } if (ictx->data_len < offsetof(QUOTA_CONTROL_ENTRY, sid)) { ntfs_error(vol->sb, "Quota defaults entry size is invalid. " "Run chkdsk."); goto err_out; } qce = (QUOTA_CONTROL_ENTRY*)ictx->data; if (le32_to_cpu(qce->version) != QUOTA_VERSION) { ntfs_error(vol->sb, "Quota defaults entry version 0x%x is not " "supported.", le32_to_cpu(qce->version)); goto err_out; } ntfs_debug("Quota defaults flags = 0x%x.", le32_to_cpu(qce->flags)); /* If quotas are already marked out of date, no need to do anything. */ if (qce->flags & QUOTA_FLAG_OUT_OF_DATE) goto set_done; /* * If quota tracking is neither requested, nor enabled and there are no * pending deletes, no need to mark the quotas out of date. */ if (!(qce->flags & (QUOTA_FLAG_TRACKING_ENABLED | QUOTA_FLAG_TRACKING_REQUESTED | QUOTA_FLAG_PENDING_DELETES))) goto set_done; /* * Set the QUOTA_FLAG_OUT_OF_DATE bit thus marking quotas out of date. * This is verified on WinXP to be sufficient to cause windows to * rescan the volume on boot and update all quota entries. */ qce->flags |= QUOTA_FLAG_OUT_OF_DATE; /* Ensure the modified flags are written to disk. */ ntfs_index_entry_flush_dcache_page(ictx); ntfs_index_entry_mark_dirty(ictx); set_done: ntfs_index_ctx_put(ictx); up(&vol->quota_q_ino->i_sem); /* * We set the flag so we do not try to mark the quotas out of date * again on remount. */ NVolSetQuotaOutOfDate(vol); done: ntfs_debug("Done."); return TRUE; err_out: if (ictx) ntfs_index_ctx_put(ictx); up(&vol->quota_q_ino->i_sem); return FALSE; }
/** * 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_add(). * * If the name is not found in the directory, a NULL inode is inserted into the * dentry @dent. 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 maintining * 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_add() @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. 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_add() 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. */ 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; uchar_t *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) { 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 (MSEQNO(mref) == NTFS_I(dent_inode)->seq_no || dent_ino == FILE_MFT) { /* Perfect WIN32/POSIX match. -- Case 1. */ if (!name) { d_add(dent, dent_inode); ntfs_debug("Done."); return NULL; } /* * 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)); if (name) 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; MFT_RECORD *m; attr_search_context *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. */ nls_name.len = (unsigned)ntfs_ucstonls(vol, (uchar_t*)&name->name, name->len, (unsigned char**)&nls_name.name, name->len * 3 + 1); kfree(name); } else /* if (name->type == FILE_NAME_DOS) */ { /* Case 3. */ FILE_NAME_ATTR *fn; 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 = get_attr_search_ctx(ni, m); if (!ctx) { err = -ENOMEM; goto err_out; } do { ATTR_RECORD *a; u32 val_len; if (!lookup_attr(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { ntfs_error(vol->sb, "Inode corrupt: No WIN32 " "namespace counterpart to DOS " "file name. Run chkdsk."); 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(uchar_t) + 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, (uchar_t*)&fn->file_name, fn->file_name_length, (unsigned char**)&nls_name.name, fn->file_name_length * 3 + 1); put_attr_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_sem 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; } d_add(real_dent, dent_inode); return real_dent; } kfree(nls_name.name); /* Matching dentry exists, check if it is negative. */ if (real_dent->d_inode) { BUG_ON(real_dent->d_inode != dent_inode); /* * Already have the inode and the dentry attached, decrement * the reference count to balance the ntfs_iget() we did * earlier on. */ iput(dent_inode); return real_dent; } /* Negative dentry: instantiate it. */ d_instantiate(real_dent, dent_inode); return real_dent; eio_err_out: ntfs_error(vol->sb, "Illegal file name attribute. Run chkdsk."); err = -EIO; err_out: if (ctx) put_attr_search_ctx(ctx); if (m) unmap_mft_record(ni); iput(dent_inode); return ERR_PTR(err); } }
/** * ntfs_mft_writepage - check if a metadata page contains dirty mft records * @page: metadata page possibly containing dirty mft records * @wbc: writeback control structure * * This is called from the VM when it wants to have a dirty $MFT/$DATA metadata * page cache page cleaned. The VM has already locked the page and marked it * clean. Instead of writing the page as a conventional ->writepage function * would do, we check if the page still contains any dirty mft records (it must * have done at some point in the past since the page was marked dirty) and if * none are found, i.e. all mft records are clean, we unlock the page and * return. The VM is then free to do with the page as it pleases. If on the * other hand we do find any dirty mft records in the page, we redirty the page * before unlocking it and returning so the VM knows that the page is still * busy and cannot be thrown out. * * Note, we do not actually write any dirty mft records here because they are * dirty inodes and hence will be written by the VFS inode dirty code paths. * There is no need to write them from the VM page dirty code paths, too and in * fact once we implement journalling it would be a complete nightmare having * two code paths leading to mft record writeout. */ static int ntfs_mft_writepage(struct page *page, struct writeback_control *wbc) { struct inode *mft_vi = page->mapping->host; struct super_block *sb = mft_vi->i_sb; ntfs_volume *vol = NTFS_SB(sb); u8 *maddr; MFT_RECORD *m; ntfs_inode **extent_nis; unsigned long mft_no; int nr, i, j; BOOL is_dirty = FALSE; BUG_ON(!PageLocked(page)); BUG_ON(PageWriteback(page)); BUG_ON(mft_vi != vol->mft_ino); /* The first mft record number in the page. */ mft_no = page->index << (PAGE_CACHE_SHIFT - vol->mft_record_size_bits); /* Number of mft records in the page. */ nr = PAGE_CACHE_SIZE >> vol->mft_record_size_bits; BUG_ON(!nr); ntfs_debug("Entering for %i inodes starting at 0x%lx.", nr, mft_no); /* Iterate over the mft records in the page looking for a dirty one. */ maddr = (u8*)kmap(page); for (i = 0; i < nr; ++i, ++mft_no, maddr += vol->mft_record_size) { struct inode *vi; ntfs_inode *ni, *eni; ntfs_attr na; na.mft_no = mft_no; na.name = NULL; na.name_len = 0; na.type = AT_UNUSED; /* * Check if the inode corresponding to this mft record is in * the VFS inode cache and obtain a reference to it if it is. */ ntfs_debug("Looking for inode 0x%lx in icache.", mft_no); /* * For inode 0, i.e. $MFT itself, we cannot use ilookup5() from * here or we deadlock because the inode is already locked by * the kernel (fs/fs-writeback.c::__sync_single_inode()) and * ilookup5() waits until the inode is unlocked before * returning it and it never gets unlocked because * ntfs_mft_writepage() never returns. )-: Fortunately, we * have inode 0 pinned in icache for the duration of the mount * so we can access it directly. */ if (!mft_no) { /* Balance the below iput(). */ vi = igrab(mft_vi); BUG_ON(vi != mft_vi); } else vi = ilookup5(sb, mft_no, (test_t)ntfs_test_inode, &na); if (vi) { ntfs_debug("Inode 0x%lx is in icache.", mft_no); /* The inode is in icache. Check if it is dirty. */ ni = NTFS_I(vi); if (!NInoDirty(ni)) { /* The inode is not dirty, skip this record. */ ntfs_debug("Inode 0x%lx is not dirty, " "continuing search.", mft_no); iput(vi); continue; } ntfs_debug("Inode 0x%lx is dirty, aborting search.", mft_no); /* The inode is dirty, no need to search further. */ iput(vi); is_dirty = TRUE; break; } ntfs_debug("Inode 0x%lx is not in icache.", mft_no); /* The inode is not in icache. */ /* Skip the record if it is not a mft record (type "FILE"). */ if (!ntfs_is_mft_recordp(maddr)) { ntfs_debug("Mft record 0x%lx is not a FILE record, " "continuing search.", mft_no); continue; } m = (MFT_RECORD*)maddr; /* * Skip the mft record if it is not in use. FIXME: What about * deleted/deallocated (extent) inodes? (AIA) */ if (!(m->flags & MFT_RECORD_IN_USE)) { ntfs_debug("Mft record 0x%lx is not in use, " "continuing search.", mft_no); continue; } /* Skip the mft record if it is a base inode. */ if (!m->base_mft_record) { ntfs_debug("Mft record 0x%lx is a base record, " "continuing search.", mft_no); continue; } /* * This is an extent mft record. Check if the inode * corresponding to its base mft record is in icache. */ na.mft_no = MREF_LE(m->base_mft_record); ntfs_debug("Mft record 0x%lx is an extent record. Looking " "for base inode 0x%lx in icache.", mft_no, na.mft_no); vi = ilookup5(sb, na.mft_no, (test_t)ntfs_test_inode, &na); if (!vi) { /* * The base inode is not in icache. Skip this extent * mft record. */ ntfs_debug("Base inode 0x%lx is not in icache, " "continuing search.", na.mft_no); continue; } ntfs_debug("Base inode 0x%lx is in icache.", na.mft_no); /* * The base inode is in icache. Check if it has the extent * inode corresponding to this extent mft record attached. */ ni = NTFS_I(vi); down(&ni->extent_lock); if (ni->nr_extents <= 0) { /* * The base inode has no attached extent inodes. Skip * this extent mft record. */ up(&ni->extent_lock); iput(vi); continue; } /* Iterate over the attached extent inodes. */ extent_nis = ni->ext.extent_ntfs_inos; for (eni = NULL, j = 0; j < ni->nr_extents; ++j) { if (mft_no == extent_nis[j]->mft_no) { /* * Found the extent inode corresponding to this * extent mft record. */ eni = extent_nis[j]; break; } } /* * If the extent inode was not attached to the base inode, skip * this extent mft record. */ if (!eni) { up(&ni->extent_lock); iput(vi); continue; } /* * Found the extent inode corrsponding to this extent mft * record. If it is dirty, no need to search further. */ if (NInoDirty(eni)) { up(&ni->extent_lock); iput(vi); is_dirty = TRUE; break; } /* The extent inode is not dirty, so do the next record. */ up(&ni->extent_lock); iput(vi); } kunmap(page); /* If a dirty mft record was found, redirty the page. */ if (is_dirty) { ntfs_debug("Inode 0x%lx is dirty. Redirtying the page " "starting at inode 0x%lx.", mft_no, page->index << (PAGE_CACHE_SHIFT - vol->mft_record_size_bits)); redirty_page_for_writepage(wbc, page); unlock_page(page); } else { /* * Keep the VM happy. This must be done otherwise the * radix-tree tag PAGECACHE_TAG_DIRTY remains set even though * the page is clean. */ BUG_ON(PageWriteback(page)); set_page_writeback(page); unlock_page(page); end_page_writeback(page); } ntfs_debug("Done."); return 0; }