| 1 | |
| 2 | |
| 3 | *
|
| 4 | * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
| 5 | *
|
| 6 | * Directory handling functions for NTFS-based filesystems.
|
| 7 | *
|
| 8 |
|
| 9 |
|
| 10 | #include <linux/fs.h>
|
| 11 | #include <linux/filelock.h>
|
| 12 | #include <linux/nls.h>
|
| 13 |
|
| 14 | #include "debug.h"
|
| 15 | #include "ntfs.h"
|
| 16 | #include "ntfs_fs.h"
|
| 17 |
|
| 18 |
|
| 19 | int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len,
|
| 20 | u8 *buf, int buf_len)
|
| 21 | {
|
| 22 | int ret, warn;
|
| 23 | u8 *op;
|
| 24 | struct nls_table *nls = sbi->options->nls;
|
| 25 |
|
| 26 | static_assert(sizeof(wchar_t) == sizeof(__le16));
|
| 27 |
|
| 28 | if (buf_len <= 0)
|
| 29 | return -EINVAL;
|
| 30 |
|
| 31 | buf_len -= 1;
|
| 32 |
|
| 33 | if (!nls) {
|
| 34 |
|
| 35 | ret = utf16s_to_utf8s((wchar_t *)name, len, UTF16_LITTLE_ENDIAN,
|
| 36 | buf, buf_len);
|
| 37 | buf[ret] = '\0';
|
| 38 | return ret;
|
| 39 | }
|
| 40 |
|
| 41 | op = buf;
|
| 42 | warn = 0;
|
| 43 |
|
| 44 | while (len--) {
|
| 45 | u16 ec;
|
| 46 | int charlen;
|
| 47 | char dump[5];
|
| 48 |
|
| 49 | if (buf_len < NLS_MAX_CHARSET_SIZE) {
|
| 50 | ntfs_warn(sbi->sb,
|
| 51 | "filename was truncated while converting.");
|
| 52 | break;
|
| 53 | }
|
| 54 |
|
| 55 | ec = le16_to_cpu(*name++);
|
| 56 | charlen = nls->uni2char(ec, op, buf_len);
|
| 57 |
|
| 58 | if (charlen > 0) {
|
| 59 | op += charlen;
|
| 60 | buf_len -= charlen;
|
| 61 | continue;
|
| 62 | }
|
| 63 |
|
| 64 | *op++ = '_';
|
| 65 | buf_len -= 1;
|
| 66 | if (warn)
|
| 67 | continue;
|
| 68 |
|
| 69 | warn = 1;
|
| 70 | hex_byte_pack(&dump[0], ec >> 8);
|
| 71 | hex_byte_pack(&dump[2], ec);
|
| 72 | dump[4] = 0;
|
| 73 |
|
| 74 | ntfs_err(sbi->sb, "failed to convert \"%s\" to %s", dump,
|
| 75 | nls->charset);
|
| 76 | }
|
| 77 |
|
| 78 | *op = '\0';
|
| 79 | return op - buf;
|
| 80 | }
|
| 81 |
|
| 82 | |
| 83 | #define PLANE_SIZE 0x00010000
|
| 84 |
|
| 85 | #define SURROGATE_PAIR 0x0000d800
|
| 86 | #define SURROGATE_LOW 0x00000400
|
| 87 | #define SURROGATE_BITS 0x000003ff
|
| 88 | |
| 89 |
|
| 90 | |
| 91 | * put_utf16 - Modified version of put_utf16 from fs/nls/nls_base.c
|
| 92 | *
|
| 93 | * Function is sparse warnings free.
|
| 94 |
|
| 95 | static inline void put_utf16(wchar_t *s, unsigned int c,
|
| 96 | enum utf16_endian endian)
|
| 97 | {
|
| 98 | static_assert(sizeof(wchar_t) == sizeof(__le16));
|
| 99 | static_assert(sizeof(wchar_t) == sizeof(__be16));
|
| 100 |
|
| 101 | switch (endian) {
|
| 102 | default:
|
| 103 | *s = (wchar_t)c;
|
| 104 | break;
|
| 105 | case UTF16_LITTLE_ENDIAN:
|
| 106 | *(__le16 *)s = __cpu_to_le16(c);
|
| 107 | break;
|
| 108 | case UTF16_BIG_ENDIAN:
|
| 109 | *(__be16 *)s = __cpu_to_be16(c);
|
| 110 | break;
|
| 111 | }
|
| 112 | }
|
| 113 |
|
| 114 | |
| 115 | * _utf8s_to_utf16s
|
| 116 | *
|
| 117 | * Modified version of 'utf8s_to_utf16s' allows to
|
| 118 | * detect -ENAMETOOLONG without writing out of expected maximum.
|
| 119 |
|
| 120 | static int _utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian,
|
| 121 | wchar_t *pwcs, int maxout)
|
| 122 | {
|
| 123 | u16 *op;
|
| 124 | int size;
|
| 125 | unicode_t u;
|
| 126 |
|
| 127 | op = pwcs;
|
| 128 | while (inlen > 0 && *s) {
|
| 129 | if (*s & 0x80) {
|
| 130 | size = utf8_to_utf32(s, inlen, &u);
|
| 131 | if (size < 0)
|
| 132 | return -EINVAL;
|
| 133 | s += size;
|
| 134 | inlen -= size;
|
| 135 |
|
| 136 | if (u >= PLANE_SIZE) {
|
| 137 | if (maxout < 2)
|
| 138 | return -ENAMETOOLONG;
|
| 139 |
|
| 140 | u -= PLANE_SIZE;
|
| 141 | put_utf16(op++,
|
| 142 | SURROGATE_PAIR |
|
| 143 | ((u >> 10) & SURROGATE_BITS),
|
| 144 | endian);
|
| 145 | put_utf16(op++,
|
| 146 | SURROGATE_PAIR | SURROGATE_LOW |
|
| 147 | (u & SURROGATE_BITS),
|
| 148 | endian);
|
| 149 | maxout -= 2;
|
| 150 | } else {
|
| 151 | if (maxout < 1)
|
| 152 | return -ENAMETOOLONG;
|
| 153 |
|
| 154 | put_utf16(op++, u, endian);
|
| 155 | maxout--;
|
| 156 | }
|
| 157 | } else {
|
| 158 | if (maxout < 1)
|
| 159 | return -ENAMETOOLONG;
|
| 160 |
|
| 161 | put_utf16(op++, *s++, endian);
|
| 162 | inlen--;
|
| 163 | maxout--;
|
| 164 | }
|
| 165 | }
|
| 166 | return op - pwcs;
|
| 167 | }
|
| 168 |
|
| 169 | |
| 170 | * ntfs_nls_to_utf16 - Convert input string to UTF-16.
|
| 171 | * @name: Input name.
|
| 172 | * @name_len: Input name length.
|
| 173 | * @uni: Destination memory.
|
| 174 | * @max_ulen: Destination memory.
|
| 175 | * @endian: Endian of target UTF-16 string.
|
| 176 | *
|
| 177 | * This function is called:
|
| 178 | * - to create NTFS name
|
| 179 | * - to create symlink
|
| 180 | *
|
| 181 | * Return: UTF-16 string length or error (if negative).
|
| 182 |
|
| 183 | int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len,
|
| 184 | struct cpu_str *uni, u32 max_ulen,
|
| 185 | enum utf16_endian endian)
|
| 186 | {
|
| 187 | int ret, slen, i;
|
| 188 | const u8 *end;
|
| 189 | struct ntfs_mount_options *opts;
|
| 190 | struct nls_table *nls;
|
| 191 | bool ads;
|
| 192 | u16 *uname = uni->name;
|
| 193 |
|
| 194 | static_assert(sizeof(wchar_t) == sizeof(u16));
|
| 195 |
|
| 196 |
|
| 197 | rcu_read_lock();
|
| 198 | opts = READ_ONCE(sbi->options);
|
| 199 | nls = opts->nls;
|
| 200 | ads = opts->ads;
|
| 201 | rcu_read_unlock();
|
| 202 |
|
| 203 | if (!nls) {
|
| 204 |
|
| 205 | ret = _utf8s_to_utf16s(name, name_len, endian, uname, max_ulen);
|
| 206 | } else {
|
| 207 | for (ret = 0, end = name + name_len; name < end;
|
| 208 | ret++, name += slen) {
|
| 209 | if (ret >= max_ulen)
|
| 210 | return -ENAMETOOLONG;
|
| 211 |
|
| 212 | slen = nls->char2uni(name, end - name, uname + ret);
|
| 213 | if (!slen)
|
| 214 | return -EINVAL;
|
| 215 | if (slen < 0)
|
| 216 | return slen;
|
| 217 | }
|
| 218 |
|
| 219 | #ifdef __BIG_ENDIAN
|
| 220 | if (endian == UTF16_LITTLE_ENDIAN) {
|
| 221 | i = ret;
|
| 222 |
|
| 223 | while (i--) {
|
| 224 | __cpu_to_le16s(uname);
|
| 225 | uname++;
|
| 226 | }
|
| 227 | }
|
| 228 | #else
|
| 229 | if (endian == UTF16_BIG_ENDIAN) {
|
| 230 | i = ret;
|
| 231 |
|
| 232 | while (i--) {
|
| 233 | __cpu_to_be16s(uname);
|
| 234 | uname++;
|
| 235 | }
|
| 236 | }
|
| 237 | #endif
|
| 238 | }
|
| 239 |
|
| 240 | uni->len = ret;
|
| 241 | uni->ads_len = 0;
|
| 242 | if (ret > 0 && ads) {
|
| 243 | uname = uni->name;
|
| 244 |
|
| 245 | for (i = 1; i + 1 < ret; i++) {
|
| 246 | if (uname[i] == ':') {
|
| 247 | uni->ads_len = ret - i - 1;
|
| 248 | uni->len = i;
|
| 249 | uname[i] = 0;
|
| 250 | ret = i;
|
| 251 |
|
| 252 | uname += i + 1;
|
| 253 | i = uni->ads_len;
|
| 254 |
|
| 255 | #ifdef __BIG_ENDIAN
|
| 256 | if (endian == UTF16_LITTLE_ENDIAN) {
|
| 257 | while (i--) {
|
| 258 | __cpu_to_le16s(uname);
|
| 259 | uname++;
|
| 260 | }
|
| 261 | }
|
| 262 | #else
|
| 263 | if (endian == UTF16_BIG_ENDIAN) {
|
| 264 | while (i--) {
|
| 265 | __cpu_to_be16s(uname);
|
| 266 | uname++;
|
| 267 | }
|
| 268 | }
|
| 269 | #endif
|
| 270 | break;
|
| 271 | }
|
| 272 | }
|
| 273 | }
|
| 274 |
|
| 275 | return ret;
|
| 276 | }
|
| 277 |
|
| 278 | |
| 279 | * dir_search_u - Helper function.
|
| 280 |
|
| 281 | struct inode *dir_search_flags(struct inode *dir, const struct cpu_str *uni,
|
| 282 | struct ntfs_fnd *fnd, u32 flags)
|
| 283 | {
|
| 284 | int err = 0;
|
| 285 | struct super_block *sb = dir->i_sb;
|
| 286 | struct ntfs_sb_info *sbi = sb->s_fs_info;
|
| 287 | struct ntfs_inode *ni = ntfs_i(dir);
|
| 288 | struct NTFS_DE *e;
|
| 289 | int diff;
|
| 290 | struct inode *inode = NULL;
|
| 291 | struct ntfs_fnd *fnd_a = NULL;
|
| 292 |
|
| 293 | if (!fnd) {
|
| 294 | fnd_a = fnd_get();
|
| 295 | if (!fnd_a) {
|
| 296 | err = -ENOMEM;
|
| 297 | goto out;
|
| 298 | }
|
| 299 | fnd = fnd_a;
|
| 300 | }
|
| 301 |
|
| 302 | err = indx_find(&ni->dir, ni, NULL, uni, 0, sbi, &diff, &e, fnd);
|
| 303 |
|
| 304 | if (err)
|
| 305 | goto out;
|
| 306 |
|
| 307 | if (diff) {
|
| 308 | err = -ENOENT;
|
| 309 | goto out;
|
| 310 | }
|
| 311 |
|
| 312 | inode = ntfs_iget5_flags(sb, &e->ref, uni, flags);
|
| 313 | if (!IS_ERR(inode) && is_bad_inode(inode)) {
|
| 314 | iput(inode);
|
| 315 | err = -EINVAL;
|
| 316 | }
|
| 317 | out:
|
| 318 | fnd_put(fnd_a);
|
| 319 |
|
| 320 | return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode;
|
| 321 | }
|
| 322 |
|
| 323 | static inline bool de_fname_fits(const struct NTFS_DE *e, u32 e_size,
|
| 324 | const struct ATTR_FILE_NAME *fname)
|
| 325 | {
|
| 326 | return sizeof(struct NTFS_DE) + fname_full_size(fname) <= e_size;
|
| 327 | }
|
| 328 |
|
| 329 | |
| 330 | * returns false if 'ctx' if full
|
| 331 |
|
| 332 | static inline bool ntfs_dir_emit(struct ntfs_sb_info *sbi,
|
| 333 | struct ntfs_inode *ni, const struct NTFS_DE *e,
|
| 334 | u8 *name, struct dir_context *ctx)
|
| 335 | {
|
| 336 | const struct ATTR_FILE_NAME *fname;
|
| 337 | u64 ino;
|
| 338 | int name_len;
|
| 339 | u32 dt_type;
|
| 340 |
|
| 341 | fname = Add2Ptr(e, sizeof(struct NTFS_DE));
|
| 342 |
|
| 343 | if (fname->type == FILE_NAME_DOS)
|
| 344 | return true;
|
| 345 |
|
| 346 | if (!mi_is_ref(&ni->mi, &fname->home))
|
| 347 | return true;
|
| 348 |
|
| 349 | ino = ino_get(&e->ref);
|
| 350 |
|
| 351 | if (ino == MFT_REC_ROOT)
|
| 352 | return true;
|
| 353 |
|
| 354 |
|
| 355 | if (!sbi->options->showmeta && ntfs_is_meta_file(sbi, ino))
|
| 356 | return true;
|
| 357 |
|
| 358 | if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
|
| 359 | return true;
|
| 360 |
|
| 361 | if (!de_fname_fits(e, le16_to_cpu(e->size), fname))
|
| 362 | return true;
|
| 363 |
|
| 364 | name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name,
|
| 365 | PATH_MAX);
|
| 366 | if (name_len <= 0) {
|
| 367 | ntfs_warn(sbi->sb, "failed to convert name for inode %llx.",
|
| 368 | ino);
|
| 369 | return true;
|
| 370 | }
|
| 371 |
|
| 372 | |
| 373 | * NTFS: symlinks are "dir + reparse" or "file + reparse"
|
| 374 | * Unfortunately reparse attribute is used for many purposes (several dozens).
|
| 375 | * It is not possible here to know is this name symlink or not.
|
| 376 | * To get exactly the type of name we should to open inode (read mft).
|
| 377 | * getattr for opened file (fstat) correctly returns symlink.
|
| 378 |
|
| 379 | dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
|
| 380 |
|
| 381 | |
| 382 | * It is not reliable to detect the type of name using duplicated information
|
| 383 | * stored in parent directory.
|
| 384 | * The only correct way to get the type of name - read MFT record and find ATTR_STD.
|
| 385 | * The code below is not good idea.
|
| 386 | * It does additional locks/reads just to get the type of name.
|
| 387 | * Should we use additional mount option to enable branch below?
|
| 388 |
|
| 389 | if (fname->dup.extend_data && ino != ni->mi.rno) {
|
| 390 | struct inode *inode = ntfs_iget5(sbi->sb, &e->ref, NULL);
|
| 391 | if (!IS_ERR_OR_NULL(inode)) {
|
| 392 | dt_type = fs_umode_to_dtype(inode->i_mode);
|
| 393 | iput(inode);
|
| 394 | }
|
| 395 | }
|
| 396 |
|
| 397 | return dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
|
| 398 | }
|
| 399 |
|
| 400 | |
| 401 | * ntfs_read_hdr - Helper function for ntfs_readdir().
|
| 402 | *
|
| 403 | * returns 0 if ok.
|
| 404 | * returns -EINVAL if directory is corrupted.
|
| 405 | * returns +1 if 'ctx' is full.
|
| 406 |
|
| 407 | static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
|
| 408 | const struct INDEX_HDR *hdr, u64 vbo, u64 pos,
|
| 409 | u8 *name, struct dir_context *ctx)
|
| 410 | {
|
| 411 | const struct NTFS_DE *e;
|
| 412 | u32 e_size;
|
| 413 | u32 end = le32_to_cpu(hdr->used);
|
| 414 | u32 off = le32_to_cpu(hdr->de_off);
|
| 415 |
|
| 416 | for (;; off += e_size) {
|
| 417 | if (off + sizeof(struct NTFS_DE) > end)
|
| 418 | return -EINVAL;
|
| 419 |
|
| 420 | e = Add2Ptr(hdr, off);
|
| 421 | e_size = le16_to_cpu(e->size);
|
| 422 | if (e_size < sizeof(struct NTFS_DE) || off + e_size > end)
|
| 423 | return -EINVAL;
|
| 424 |
|
| 425 | if (de_is_last(e))
|
| 426 | return 0;
|
| 427 |
|
| 428 |
|
| 429 | if (vbo + off < pos)
|
| 430 | continue;
|
| 431 |
|
| 432 | if (le16_to_cpu(e->key_size) < SIZEOF_ATTRIBUTE_FILENAME)
|
| 433 | return -EINVAL;
|
| 434 |
|
| 435 | ctx->pos = vbo + off;
|
| 436 |
|
| 437 |
|
| 438 | if (!ntfs_dir_emit(sbi, ni, e, name, ctx)) {
|
| 439 |
|
| 440 | return +1;
|
| 441 | }
|
| 442 | }
|
| 443 | }
|
| 444 |
|
| 445 | |
| 446 | * ntfs_readdir - file_operations::iterate_shared
|
| 447 | *
|
| 448 | * Use non sorted enumeration.
|
| 449 | * Sorted enumeration may result infinite loop if names tree contains loop.
|
| 450 |
|
| 451 | static int ntfs_readdir(struct file *file, struct dir_context *ctx)
|
| 452 | {
|
| 453 | const struct INDEX_ROOT *root;
|
| 454 | size_t bit;
|
| 455 | int err = 0;
|
| 456 | struct inode *dir = file_inode(file);
|
| 457 | struct ntfs_inode *ni = ntfs_i(dir);
|
| 458 | struct super_block *sb = dir->i_sb;
|
| 459 | struct ntfs_sb_info *sbi = sb->s_fs_info;
|
| 460 | loff_t i_size = i_size_read(dir);
|
| 461 | u64 pos = ctx->pos;
|
| 462 | u8 *name = NULL;
|
| 463 | struct indx_node *node = NULL;
|
| 464 | u8 index_bits = ni->dir.index_bits;
|
| 465 | size_t max_bit = i_size >> ni->dir.index_bits;
|
| 466 | loff_t eod = i_size + sbi->record_size;
|
| 467 |
|
| 468 |
|
| 469 | static_assert(NTFS_NAME_LEN * 4 < PATH_MAX);
|
| 470 |
|
| 471 | if (!pos) {
|
| 472 | |
| 473 | * ni->dir.version increments each directory change.
|
| 474 | * Save the initial value of ni->dir.version.
|
| 475 |
|
| 476 | file->private_data = (void *)ni->dir.version;
|
| 477 | }
|
| 478 |
|
| 479 | if (pos >= eod) {
|
| 480 | if (file->private_data == (void *)ni->dir.version) {
|
| 481 |
|
| 482 | return 0;
|
| 483 | }
|
| 484 |
|
| 485 | |
| 486 | * Handle directories that changed after the initial readdir().
|
| 487 | *
|
| 488 | * Some user space code implements recursive removal like this instead
|
| 489 | * of calling rmdir(2) directly:
|
| 490 | *
|
| 491 | * fd = opendir(path);
|
| 492 | * while ((dent = readdir(fd)))
|
| 493 | * unlinkat(dirfd(fd), dent->d_name, 0);
|
| 494 | * closedir(fd);
|
| 495 | *
|
| 496 | * POSIX leaves unspecified what readdir() should return once the
|
| 497 | * directory has been modified after opendir()/rewinddir(), so this
|
| 498 | * pattern is not guaranteed to work on all filesystems or platforms.
|
| 499 | *
|
| 500 | * In ntfs3 the internal name tree may be reshaped while entries are
|
| 501 | * being removed, so there is no stable anchor for continuing a
|
| 502 | * single-pass walk based on the original readdir() order.
|
| 503 | *
|
| 504 | * In practice some widely used tools (for example certain rm(1)
|
| 505 | * implementations) have used this readdir()/unlink() loop, and some
|
| 506 | * filesystems behave in a way that effectively makes it work in the
|
| 507 | * common case.
|
| 508 | *
|
| 509 | * The code below follows that practice and tries to provide
|
| 510 | * "rmdir-like" behaviour for such callers on ntfs3, even though the
|
| 511 | * situation is not strictly defined by the APIs.
|
| 512 | *
|
| 513 | * Apple documents the same readdir()/unlink() issue and a workaround
|
| 514 | * for HFS file systems in:
|
| 515 | * https://web.archive.org/web/20220122122948/https:/support.apple.com/kb/TA21420?locale=en_US
|
| 516 |
|
| 517 | ctx->pos = pos = 3;
|
| 518 | file->private_data = (void *)ni->dir.version;
|
| 519 | }
|
| 520 |
|
| 521 | if (!dir_emit_dots(file, ctx))
|
| 522 | return 0;
|
| 523 |
|
| 524 | name = kmalloc(PATH_MAX, GFP_KERNEL);
|
| 525 | if (!name)
|
| 526 | return -ENOMEM;
|
| 527 |
|
| 528 | if (!ni->mi_loaded && ni->attr_list.size) {
|
| 529 | |
| 530 | * Directory inode is locked for read.
|
| 531 | * Load all subrecords to avoid 'write' access to 'ni' during
|
| 532 | * directory reading.
|
| 533 |
|
| 534 | ni_lock(ni);
|
| 535 | if (!ni->mi_loaded && ni->attr_list.size) {
|
| 536 | err = ni_load_all_mi(ni);
|
| 537 | if (!err)
|
| 538 | ni->mi_loaded = true;
|
| 539 | }
|
| 540 | ni_unlock(ni);
|
| 541 | if (err)
|
| 542 | goto out;
|
| 543 | }
|
| 544 |
|
| 545 | |
| 546 | * Keep directory metadata stable for the whole walk. Loading subrecords
|
| 547 | * once is not enough if concurrent writeback can still compact ATTR_LIST
|
| 548 | * entries and free the record that ntfs_read_hdr() is currently walking.
|
| 549 |
|
| 550 | ni_lock(ni);
|
| 551 |
|
| 552 | root = indx_get_root(&ni->dir, ni, NULL, NULL);
|
| 553 | if (!root) {
|
| 554 | err = -EINVAL;
|
| 555 | goto out_unlock;
|
| 556 | }
|
| 557 |
|
| 558 | if (pos >= sbi->record_size) {
|
| 559 | bit = (pos - sbi->record_size) >> index_bits;
|
| 560 | } else {
|
| 561 | |
| 562 | * Add each name from root in 'ctx'.
|
| 563 |
|
| 564 | err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
|
| 565 | if (err)
|
| 566 | goto out_unlock;
|
| 567 | bit = 0;
|
| 568 | }
|
| 569 |
|
| 570 | |
| 571 | * Enumerate indexes until the end of dir.
|
| 572 |
|
| 573 | for (; bit < max_bit; bit += 1) {
|
| 574 |
|
| 575 | err = indx_used_bit(&ni->dir, ni, &bit);
|
| 576 | if (err)
|
| 577 | goto out_unlock;
|
| 578 |
|
| 579 | if (bit == MINUS_ONE_T) {
|
| 580 |
|
| 581 | break;
|
| 582 | }
|
| 583 |
|
| 584 | if (bit >= max_bit) {
|
| 585 |
|
| 586 | err = -EINVAL;
|
| 587 | goto out_unlock;
|
| 588 | }
|
| 589 |
|
| 590 | err = indx_read_ra(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
|
| 591 | &node, &file->f_ra);
|
| 592 | if (err)
|
| 593 | goto out_unlock;
|
| 594 |
|
| 595 | |
| 596 | * Add each name from index in 'ctx'.
|
| 597 |
|
| 598 | err = ntfs_read_hdr(sbi, ni, &node->index->ihdr,
|
| 599 | ((u64)bit << index_bits) + sbi->record_size,
|
| 600 | pos, name, ctx);
|
| 601 | if (err)
|
| 602 | goto out_unlock;
|
| 603 | }
|
| 604 |
|
| 605 | out_unlock:
|
| 606 | ni_unlock(ni);
|
| 607 |
|
| 608 | out:
|
| 609 | kfree(name);
|
| 610 | put_indx_node(node);
|
| 611 |
|
| 612 | if (!err) {
|
| 613 |
|
| 614 | ctx->pos = eod;
|
| 615 | } else if (err == 1) {
|
| 616 |
|
| 617 | err = 0;
|
| 618 | } else if (err == -ENOENT) {
|
| 619 | err = 0;
|
| 620 | ctx->pos = pos;
|
| 621 | } else if (err < 0) {
|
| 622 | if (err == -EINVAL)
|
| 623 | _ntfs_bad_inode(dir);
|
| 624 | ctx->pos = eod;
|
| 625 | }
|
| 626 |
|
| 627 | return err;
|
| 628 | }
|
| 629 |
|
| 630 | |
| 631 | * Return fname when @e passes the same checks as ntfs_dir_emit() before
|
| 632 | * exposing an entry (valid key, non-DOS, fname fits in e->size).
|
| 633 |
|
| 634 | static inline const struct ATTR_FILE_NAME *
|
| 635 | de_countable_fname(const struct NTFS_DE *e, u32 e_size)
|
| 636 | {
|
| 637 | const struct ATTR_FILE_NAME *fname;
|
| 638 |
|
| 639 | fname = de_get_fname(e);
|
| 640 | if (!fname || fname->type == FILE_NAME_DOS ||
|
| 641 | !de_fname_fits(e, e_size, fname))
|
| 642 | return NULL;
|
| 643 |
|
| 644 | return fname;
|
| 645 | }
|
| 646 |
|
| 647 | static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
|
| 648 | size_t *files)
|
| 649 | {
|
| 650 | int err = 0;
|
| 651 | struct ntfs_inode *ni = ntfs_i(dir);
|
| 652 | struct NTFS_DE *e = NULL;
|
| 653 | struct INDEX_ROOT *root;
|
| 654 | struct INDEX_HDR *hdr;
|
| 655 | const struct ATTR_FILE_NAME *fname;
|
| 656 | u32 e_size, off, end;
|
| 657 | size_t drs = 0, fles = 0, bit = 0;
|
| 658 | struct indx_node *node = NULL;
|
| 659 | size_t max_indx = i_size_read(&ni->vfs_inode) >> ni->dir.index_bits;
|
| 660 |
|
| 661 | if (is_empty)
|
| 662 | *is_empty = true;
|
| 663 |
|
| 664 | root = indx_get_root(&ni->dir, ni, NULL, NULL);
|
| 665 | if (!root)
|
| 666 | return -EINVAL;
|
| 667 |
|
| 668 | hdr = &root->ihdr;
|
| 669 |
|
| 670 | for (;;) {
|
| 671 | end = le32_to_cpu(hdr->used);
|
| 672 | off = le32_to_cpu(hdr->de_off);
|
| 673 |
|
| 674 | for (; off + sizeof(struct NTFS_DE) <= end; off += e_size) {
|
| 675 | e = Add2Ptr(hdr, off);
|
| 676 | e_size = le16_to_cpu(e->size);
|
| 677 | if (e_size < sizeof(struct NTFS_DE) ||
|
| 678 | off + e_size > end) {
|
| 679 |
|
| 680 | break;
|
| 681 | }
|
| 682 |
|
| 683 | if (de_is_last(e))
|
| 684 | break;
|
| 685 |
|
| 686 | fname = de_countable_fname(e, e_size);
|
| 687 | if (!fname)
|
| 688 | continue;
|
| 689 |
|
| 690 | if (is_empty) {
|
| 691 | *is_empty = false;
|
| 692 | if (!dirs && !files)
|
| 693 | goto out;
|
| 694 | }
|
| 695 |
|
| 696 | if (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY)
|
| 697 | drs += 1;
|
| 698 | else
|
| 699 | fles += 1;
|
| 700 | }
|
| 701 |
|
| 702 | if (bit >= max_indx)
|
| 703 | goto out;
|
| 704 |
|
| 705 | err = indx_used_bit(&ni->dir, ni, &bit);
|
| 706 | if (err)
|
| 707 | goto out;
|
| 708 |
|
| 709 | if (bit == MINUS_ONE_T)
|
| 710 | goto out;
|
| 711 |
|
| 712 | if (bit >= max_indx)
|
| 713 | goto out;
|
| 714 |
|
| 715 | err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
|
| 716 | &node);
|
| 717 | if (err)
|
| 718 | goto out;
|
| 719 |
|
| 720 | hdr = &node->index->ihdr;
|
| 721 | bit += 1;
|
| 722 | }
|
| 723 |
|
| 724 | out:
|
| 725 | put_indx_node(node);
|
| 726 | if (dirs)
|
| 727 | *dirs = drs;
|
| 728 | if (files)
|
| 729 | *files = fles;
|
| 730 |
|
| 731 | return err;
|
| 732 | }
|
| 733 |
|
| 734 | bool dir_is_empty(struct inode *dir)
|
| 735 | {
|
| 736 | bool is_empty = false;
|
| 737 |
|
| 738 | ntfs_dir_count(dir, &is_empty, NULL, NULL);
|
| 739 |
|
| 740 | return is_empty;
|
| 741 | }
|
| 742 |
|
| 743 | |
| 744 | const struct file_operations ntfs_dir_operations = {
|
| 745 | .llseek = generic_file_llseek,
|
| 746 | .read = generic_read_dir,
|
| 747 | .iterate_shared = ntfs_readdir,
|
| 748 | .fsync = ntfs_file_fsync,
|
| 749 | .open = ntfs_file_open,
|
| 750 | .unlocked_ioctl = ntfs_ioctl,
|
| 751 | #ifdef CONFIG_COMPAT
|
| 752 | .compat_ioctl = ntfs_compat_ioctl,
|
| 753 | #endif
|
| 754 | .setlease = generic_setlease,
|
| 755 | };
|
| 756 | |