Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | // SPDX-License-Identifier: GPL-2.0-or-later /* * namei.c - NTFS kernel directory inode operations. Part of the Linux-NTFS * project. * * Copyright (c) 2001-2006 Anton Altaparmakov */ #include <linux/dcache.h> #include <linux/exportfs.h> #include <linux/security.h> #include <linux/slab.h> #include "attrib.h" #include "debug.h" #include "dir.h" #include "mft.h" #include "ntfs.h" /** * 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 (!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); } } /* * Inode operations for directories. */ const struct inode_operations ntfs_dir_inode_ops = { .lookup = ntfs_lookup, /* VFS: Lookup directory. */ }; /** * 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 @d_inode(child_dent)->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 = d_inode(child_dent); 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); /* Get the mft record of the inode belonging to the child dentry. */ mrec = map_mft_record(ni); if (IS_ERR(mrec)) return ERR_CAST(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); return d_obtain_alias(ntfs_iget(vi->i_sb, parent_ino)); } static struct inode *ntfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation) { struct inode *inode; inode = ntfs_iget(sb, ino); if (!IS_ERR(inode)) { if (is_bad_inode(inode) || inode->i_generation != generation) { iput(inode); inode = ERR_PTR(-ESTALE); } } return inode; } static struct dentry *ntfs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ntfs_nfs_get_inode); } static struct dentry *ntfs_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { return generic_fh_to_parent(sb, fid, fh_len, fh_type, ntfs_nfs_get_inode); } /* * Export operations allowing NFS exporting of mounted NTFS partitions. * * We use the default ->encode_fh() for now. Note that they * use 32 bits to store the inode number which is an unsigned long so on 64-bit * architectures is usually 64 bits so it would all fail horribly on huge * volumes. I guess we need to define our own encode and decode fh functions * that store 64-bit inode numbers at some point but for now we will ignore the * problem... * * We also use the default ->get_name() helper (used by ->decode_fh() via * fs/exportfs/expfs.c::find_exported_dentry()) as that is completely fs * independent. * * The default ->get_parent() just returns -EACCES so we have to provide our * own and the default ->get_dentry() is incompatible with NTFS due to not * allowing the inode number 0 which is used in NTFS for the system file $MFT * and due to using iget() whereas NTFS needs ntfs_iget(). */ const struct export_operations ntfs_export_ops = { .get_parent = ntfs_get_parent, /* Find the parent of a given directory. */ .fh_to_dentry = ntfs_fh_to_dentry, .fh_to_parent = ntfs_fh_to_parent, }; |