linux/fs/ntfs3/dir.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   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/nls.h>
  12
  13#include "debug.h"
  14#include "ntfs.h"
  15#include "ntfs_fs.h"
  16
  17/* Convert little endian UTF-16 to NLS string. */
  18int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len,
  19                      u8 *buf, int buf_len)
  20{
  21        int ret, warn;
  22        u8 *op;
  23        struct nls_table *nls = sbi->options->nls;
  24
  25        static_assert(sizeof(wchar_t) == sizeof(__le16));
  26
  27        if (!nls) {
  28                /* UTF-16 -> UTF-8 */
  29                ret = utf16s_to_utf8s(name, len, UTF16_LITTLE_ENDIAN, buf,
  30                                      buf_len);
  31                buf[ret] = '\0';
  32                return ret;
  33        }
  34
  35        op = buf;
  36        warn = 0;
  37
  38        while (len--) {
  39                u16 ec;
  40                int charlen;
  41                char dump[5];
  42
  43                if (buf_len < NLS_MAX_CHARSET_SIZE) {
  44                        ntfs_warn(sbi->sb,
  45                                  "filename was truncated while converting.");
  46                        break;
  47                }
  48
  49                ec = le16_to_cpu(*name++);
  50                charlen = nls->uni2char(ec, op, buf_len);
  51
  52                if (charlen > 0) {
  53                        op += charlen;
  54                        buf_len -= charlen;
  55                        continue;
  56                }
  57
  58                *op++ = '_';
  59                buf_len -= 1;
  60                if (warn)
  61                        continue;
  62
  63                warn = 1;
  64                hex_byte_pack(&dump[0], ec >> 8);
  65                hex_byte_pack(&dump[2], ec);
  66                dump[4] = 0;
  67
  68                ntfs_err(sbi->sb, "failed to convert \"%s\" to %s", dump,
  69                         nls->charset);
  70        }
  71
  72        *op = '\0';
  73        return op - buf;
  74}
  75
  76// clang-format off
  77#define PLANE_SIZE      0x00010000
  78
  79#define SURROGATE_PAIR  0x0000d800
  80#define SURROGATE_LOW   0x00000400
  81#define SURROGATE_BITS  0x000003ff
  82// clang-format on
  83
  84/*
  85 * put_utf16 - Modified version of put_utf16 from fs/nls/nls_base.c
  86 *
  87 * Function is sparse warnings free.
  88 */
  89static inline void put_utf16(wchar_t *s, unsigned int c,
  90                             enum utf16_endian endian)
  91{
  92        static_assert(sizeof(wchar_t) == sizeof(__le16));
  93        static_assert(sizeof(wchar_t) == sizeof(__be16));
  94
  95        switch (endian) {
  96        default:
  97                *s = (wchar_t)c;
  98                break;
  99        case UTF16_LITTLE_ENDIAN:
 100                *(__le16 *)s = __cpu_to_le16(c);
 101                break;
 102        case UTF16_BIG_ENDIAN:
 103                *(__be16 *)s = __cpu_to_be16(c);
 104                break;
 105        }
 106}
 107
 108/*
 109 * _utf8s_to_utf16s
 110 *
 111 * Modified version of 'utf8s_to_utf16s' allows to
 112 * detect -ENAMETOOLONG without writing out of expected maximum.
 113 */
 114static int _utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian,
 115                            wchar_t *pwcs, int maxout)
 116{
 117        u16 *op;
 118        int size;
 119        unicode_t u;
 120
 121        op = pwcs;
 122        while (inlen > 0 && *s) {
 123                if (*s & 0x80) {
 124                        size = utf8_to_utf32(s, inlen, &u);
 125                        if (size < 0)
 126                                return -EINVAL;
 127                        s += size;
 128                        inlen -= size;
 129
 130                        if (u >= PLANE_SIZE) {
 131                                if (maxout < 2)
 132                                        return -ENAMETOOLONG;
 133
 134                                u -= PLANE_SIZE;
 135                                put_utf16(op++,
 136                                          SURROGATE_PAIR |
 137                                                  ((u >> 10) & SURROGATE_BITS),
 138                                          endian);
 139                                put_utf16(op++,
 140                                          SURROGATE_PAIR | SURROGATE_LOW |
 141                                                  (u & SURROGATE_BITS),
 142                                          endian);
 143                                maxout -= 2;
 144                        } else {
 145                                if (maxout < 1)
 146                                        return -ENAMETOOLONG;
 147
 148                                put_utf16(op++, u, endian);
 149                                maxout--;
 150                        }
 151                } else {
 152                        if (maxout < 1)
 153                                return -ENAMETOOLONG;
 154
 155                        put_utf16(op++, *s++, endian);
 156                        inlen--;
 157                        maxout--;
 158                }
 159        }
 160        return op - pwcs;
 161}
 162
 163/*
 164 * ntfs_nls_to_utf16 - Convert input string to UTF-16.
 165 * @name:       Input name.
 166 * @name_len:   Input name length.
 167 * @uni:        Destination memory.
 168 * @max_ulen:   Destination memory.
 169 * @endian:     Endian of target UTF-16 string.
 170 *
 171 * This function is called:
 172 * - to create NTFS name
 173 * - to create symlink
 174 *
 175 * Return: UTF-16 string length or error (if negative).
 176 */
 177int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len,
 178                      struct cpu_str *uni, u32 max_ulen,
 179                      enum utf16_endian endian)
 180{
 181        int ret, slen;
 182        const u8 *end;
 183        struct nls_table *nls = sbi->options->nls;
 184        u16 *uname = uni->name;
 185
 186        static_assert(sizeof(wchar_t) == sizeof(u16));
 187
 188        if (!nls) {
 189                /* utf8 -> utf16 */
 190                ret = _utf8s_to_utf16s(name, name_len, endian, uname, max_ulen);
 191                uni->len = ret;
 192                return ret;
 193        }
 194
 195        for (ret = 0, end = name + name_len; name < end; ret++, name += slen) {
 196                if (ret >= max_ulen)
 197                        return -ENAMETOOLONG;
 198
 199                slen = nls->char2uni(name, end - name, uname + ret);
 200                if (!slen)
 201                        return -EINVAL;
 202                if (slen < 0)
 203                        return slen;
 204        }
 205
 206#ifdef __BIG_ENDIAN
 207        if (endian == UTF16_LITTLE_ENDIAN) {
 208                int i = ret;
 209
 210                while (i--) {
 211                        __cpu_to_le16s(uname);
 212                        uname++;
 213                }
 214        }
 215#else
 216        if (endian == UTF16_BIG_ENDIAN) {
 217                int i = ret;
 218
 219                while (i--) {
 220                        __cpu_to_be16s(uname);
 221                        uname++;
 222                }
 223        }
 224#endif
 225
 226        uni->len = ret;
 227        return ret;
 228}
 229
 230/*
 231 * dir_search_u - Helper function.
 232 */
 233struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni,
 234                           struct ntfs_fnd *fnd)
 235{
 236        int err = 0;
 237        struct super_block *sb = dir->i_sb;
 238        struct ntfs_sb_info *sbi = sb->s_fs_info;
 239        struct ntfs_inode *ni = ntfs_i(dir);
 240        struct NTFS_DE *e;
 241        int diff;
 242        struct inode *inode = NULL;
 243        struct ntfs_fnd *fnd_a = NULL;
 244
 245        if (!fnd) {
 246                fnd_a = fnd_get();
 247                if (!fnd_a) {
 248                        err = -ENOMEM;
 249                        goto out;
 250                }
 251                fnd = fnd_a;
 252        }
 253
 254        err = indx_find(&ni->dir, ni, NULL, uni, 0, sbi, &diff, &e, fnd);
 255
 256        if (err)
 257                goto out;
 258
 259        if (diff) {
 260                err = -ENOENT;
 261                goto out;
 262        }
 263
 264        inode = ntfs_iget5(sb, &e->ref, uni);
 265        if (!IS_ERR(inode) && is_bad_inode(inode)) {
 266                iput(inode);
 267                err = -EINVAL;
 268        }
 269out:
 270        fnd_put(fnd_a);
 271
 272        return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode;
 273}
 274
 275static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
 276                               const struct NTFS_DE *e, u8 *name,
 277                               struct dir_context *ctx)
 278{
 279        const struct ATTR_FILE_NAME *fname;
 280        unsigned long ino;
 281        int name_len;
 282        u32 dt_type;
 283
 284        fname = Add2Ptr(e, sizeof(struct NTFS_DE));
 285
 286        if (fname->type == FILE_NAME_DOS)
 287                return 0;
 288
 289        if (!mi_is_ref(&ni->mi, &fname->home))
 290                return 0;
 291
 292        ino = ino_get(&e->ref);
 293
 294        if (ino == MFT_REC_ROOT)
 295                return 0;
 296
 297        /* Skip meta files. Unless option to show metafiles is set. */
 298        if (!sbi->options->showmeta && ntfs_is_meta_file(sbi, ino))
 299                return 0;
 300
 301        if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
 302                return 0;
 303
 304        name_len = ntfs_utf16_to_nls(sbi, fname->name, fname->name_len, name,
 305                                     PATH_MAX);
 306        if (name_len <= 0) {
 307                ntfs_warn(sbi->sb, "failed to convert name for inode %lx.",
 308                          ino);
 309                return 0;
 310        }
 311
 312        dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
 313
 314        return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
 315}
 316
 317/*
 318 * ntfs_read_hdr - Helper function for ntfs_readdir().
 319 */
 320static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
 321                         const struct INDEX_HDR *hdr, u64 vbo, u64 pos,
 322                         u8 *name, struct dir_context *ctx)
 323{
 324        int err;
 325        const struct NTFS_DE *e;
 326        u32 e_size;
 327        u32 end = le32_to_cpu(hdr->used);
 328        u32 off = le32_to_cpu(hdr->de_off);
 329
 330        for (;; off += e_size) {
 331                if (off + sizeof(struct NTFS_DE) > end)
 332                        return -1;
 333
 334                e = Add2Ptr(hdr, off);
 335                e_size = le16_to_cpu(e->size);
 336                if (e_size < sizeof(struct NTFS_DE) || off + e_size > end)
 337                        return -1;
 338
 339                if (de_is_last(e))
 340                        return 0;
 341
 342                /* Skip already enumerated. */
 343                if (vbo + off < pos)
 344                        continue;
 345
 346                if (le16_to_cpu(e->key_size) < SIZEOF_ATTRIBUTE_FILENAME)
 347                        return -1;
 348
 349                ctx->pos = vbo + off;
 350
 351                /* Submit the name to the filldir callback. */
 352                err = ntfs_filldir(sbi, ni, e, name, ctx);
 353                if (err)
 354                        return err;
 355        }
 356}
 357
 358/*
 359 * ntfs_readdir - file_operations::iterate_shared
 360 *
 361 * Use non sorted enumeration.
 362 * We have an example of broken volume where sorted enumeration
 363 * counts each name twice.
 364 */
 365static int ntfs_readdir(struct file *file, struct dir_context *ctx)
 366{
 367        const struct INDEX_ROOT *root;
 368        u64 vbo;
 369        size_t bit;
 370        loff_t eod;
 371        int err = 0;
 372        struct inode *dir = file_inode(file);
 373        struct ntfs_inode *ni = ntfs_i(dir);
 374        struct super_block *sb = dir->i_sb;
 375        struct ntfs_sb_info *sbi = sb->s_fs_info;
 376        loff_t i_size = i_size_read(dir);
 377        u32 pos = ctx->pos;
 378        u8 *name = NULL;
 379        struct indx_node *node = NULL;
 380        u8 index_bits = ni->dir.index_bits;
 381
 382        /* Name is a buffer of PATH_MAX length. */
 383        static_assert(NTFS_NAME_LEN * 4 < PATH_MAX);
 384
 385        eod = i_size + sbi->record_size;
 386
 387        if (pos >= eod)
 388                return 0;
 389
 390        if (!dir_emit_dots(file, ctx))
 391                return 0;
 392
 393        /* Allocate PATH_MAX bytes. */
 394        name = __getname();
 395        if (!name)
 396                return -ENOMEM;
 397
 398        if (!ni->mi_loaded && ni->attr_list.size) {
 399                /*
 400                 * Directory inode is locked for read.
 401                 * Load all subrecords to avoid 'write' access to 'ni' during
 402                 * directory reading.
 403                 */
 404                ni_lock(ni);
 405                if (!ni->mi_loaded && ni->attr_list.size) {
 406                        err = ni_load_all_mi(ni);
 407                        if (!err)
 408                                ni->mi_loaded = true;
 409                }
 410                ni_unlock(ni);
 411                if (err)
 412                        goto out;
 413        }
 414
 415        root = indx_get_root(&ni->dir, ni, NULL, NULL);
 416        if (!root) {
 417                err = -EINVAL;
 418                goto out;
 419        }
 420
 421        if (pos >= sbi->record_size) {
 422                bit = (pos - sbi->record_size) >> index_bits;
 423        } else {
 424                err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
 425                if (err)
 426                        goto out;
 427                bit = 0;
 428        }
 429
 430        if (!i_size) {
 431                ctx->pos = eod;
 432                goto out;
 433        }
 434
 435        for (;;) {
 436                vbo = (u64)bit << index_bits;
 437                if (vbo >= i_size) {
 438                        ctx->pos = eod;
 439                        goto out;
 440                }
 441
 442                err = indx_used_bit(&ni->dir, ni, &bit);
 443                if (err)
 444                        goto out;
 445
 446                if (bit == MINUS_ONE_T) {
 447                        ctx->pos = eod;
 448                        goto out;
 449                }
 450
 451                vbo = (u64)bit << index_bits;
 452                if (vbo >= i_size) {
 453                        ntfs_inode_err(dir, "Looks like your dir is corrupt");
 454                        err = -EINVAL;
 455                        goto out;
 456                }
 457
 458                err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
 459                                &node);
 460                if (err)
 461                        goto out;
 462
 463                err = ntfs_read_hdr(sbi, ni, &node->index->ihdr,
 464                                    vbo + sbi->record_size, pos, name, ctx);
 465                if (err)
 466                        goto out;
 467
 468                bit += 1;
 469        }
 470
 471out:
 472
 473        __putname(name);
 474        put_indx_node(node);
 475
 476        if (err == -ENOENT) {
 477                err = 0;
 478                ctx->pos = pos;
 479        }
 480
 481        return err;
 482}
 483
 484static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
 485                          size_t *files)
 486{
 487        int err = 0;
 488        struct ntfs_inode *ni = ntfs_i(dir);
 489        struct NTFS_DE *e = NULL;
 490        struct INDEX_ROOT *root;
 491        struct INDEX_HDR *hdr;
 492        const struct ATTR_FILE_NAME *fname;
 493        u32 e_size, off, end;
 494        u64 vbo = 0;
 495        size_t drs = 0, fles = 0, bit = 0;
 496        loff_t i_size = ni->vfs_inode.i_size;
 497        struct indx_node *node = NULL;
 498        u8 index_bits = ni->dir.index_bits;
 499
 500        if (is_empty)
 501                *is_empty = true;
 502
 503        root = indx_get_root(&ni->dir, ni, NULL, NULL);
 504        if (!root)
 505                return -EINVAL;
 506
 507        hdr = &root->ihdr;
 508
 509        for (;;) {
 510                end = le32_to_cpu(hdr->used);
 511                off = le32_to_cpu(hdr->de_off);
 512
 513                for (; off + sizeof(struct NTFS_DE) <= end; off += e_size) {
 514                        e = Add2Ptr(hdr, off);
 515                        e_size = le16_to_cpu(e->size);
 516                        if (e_size < sizeof(struct NTFS_DE) ||
 517                            off + e_size > end)
 518                                break;
 519
 520                        if (de_is_last(e))
 521                                break;
 522
 523                        fname = de_get_fname(e);
 524                        if (!fname)
 525                                continue;
 526
 527                        if (fname->type == FILE_NAME_DOS)
 528                                continue;
 529
 530                        if (is_empty) {
 531                                *is_empty = false;
 532                                if (!dirs && !files)
 533                                        goto out;
 534                        }
 535
 536                        if (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY)
 537                                drs += 1;
 538                        else
 539                                fles += 1;
 540                }
 541
 542                if (vbo >= i_size)
 543                        goto out;
 544
 545                err = indx_used_bit(&ni->dir, ni, &bit);
 546                if (err)
 547                        goto out;
 548
 549                if (bit == MINUS_ONE_T)
 550                        goto out;
 551
 552                vbo = (u64)bit << index_bits;
 553                if (vbo >= i_size)
 554                        goto out;
 555
 556                err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
 557                                &node);
 558                if (err)
 559                        goto out;
 560
 561                hdr = &node->index->ihdr;
 562                bit += 1;
 563                vbo = (u64)bit << ni->dir.idx2vbn_bits;
 564        }
 565
 566out:
 567        put_indx_node(node);
 568        if (dirs)
 569                *dirs = drs;
 570        if (files)
 571                *files = fles;
 572
 573        return err;
 574}
 575
 576bool dir_is_empty(struct inode *dir)
 577{
 578        bool is_empty = false;
 579
 580        ntfs_dir_count(dir, &is_empty, NULL, NULL);
 581
 582        return is_empty;
 583}
 584
 585// clang-format off
 586const struct file_operations ntfs_dir_operations = {
 587        .llseek         = generic_file_llseek,
 588        .read           = generic_read_dir,
 589        .iterate_shared = ntfs_readdir,
 590        .fsync          = generic_file_fsync,
 591        .open           = ntfs_file_open,
 592};
 593// clang-format on
 594