linux/fs/hfsplus/dir.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/hfsplus/dir.c
   3 *
   4 * Copyright (C) 2001
   5 * Brad Boyer (flar@allandria.com)
   6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
   7 *
   8 * Handling of directories
   9 */
  10
  11#include <linux/errno.h>
  12#include <linux/fs.h>
  13#include <linux/slab.h>
  14#include <linux/random.h>
  15
  16#include "hfsplus_fs.h"
  17#include "hfsplus_raw.h"
  18#include "xattr.h"
  19#include "acl.h"
  20
  21static inline void hfsplus_instantiate(struct dentry *dentry,
  22                                       struct inode *inode, u32 cnid)
  23{
  24        dentry->d_fsdata = (void *)(unsigned long)cnid;
  25        d_instantiate(dentry, inode);
  26}
  27
  28/* Find the entry inside dir named dentry->d_name */
  29static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
  30                                     unsigned int flags)
  31{
  32        struct inode *inode = NULL;
  33        struct hfs_find_data fd;
  34        struct super_block *sb;
  35        hfsplus_cat_entry entry;
  36        int err;
  37        u32 cnid, linkid = 0;
  38        u16 type;
  39
  40        sb = dir->i_sb;
  41
  42        dentry->d_fsdata = NULL;
  43        err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
  44        if (err)
  45                return ERR_PTR(err);
  46        hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
  47again:
  48        err = hfs_brec_read(&fd, &entry, sizeof(entry));
  49        if (err) {
  50                if (err == -ENOENT) {
  51                        hfs_find_exit(&fd);
  52                        /* No such entry */
  53                        inode = NULL;
  54                        goto out;
  55                }
  56                goto fail;
  57        }
  58        type = be16_to_cpu(entry.type);
  59        if (type == HFSPLUS_FOLDER) {
  60                if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
  61                        err = -EIO;
  62                        goto fail;
  63                }
  64                cnid = be32_to_cpu(entry.folder.id);
  65                dentry->d_fsdata = (void *)(unsigned long)cnid;
  66        } else if (type == HFSPLUS_FILE) {
  67                if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
  68                        err = -EIO;
  69                        goto fail;
  70                }
  71                cnid = be32_to_cpu(entry.file.id);
  72                if (entry.file.user_info.fdType ==
  73                                cpu_to_be32(HFSP_HARDLINK_TYPE) &&
  74                                entry.file.user_info.fdCreator ==
  75                                cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
  76                                (entry.file.create_date ==
  77                                        HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
  78                                                create_date ||
  79                                entry.file.create_date ==
  80                                        HFSPLUS_I(sb->s_root->d_inode)->
  81                                                create_date) &&
  82                                HFSPLUS_SB(sb)->hidden_dir) {
  83                        struct qstr str;
  84                        char name[32];
  85
  86                        if (dentry->d_fsdata) {
  87                                /*
  88                                 * We found a link pointing to another link,
  89                                 * so ignore it and treat it as regular file.
  90                                 */
  91                                cnid = (unsigned long)dentry->d_fsdata;
  92                                linkid = 0;
  93                        } else {
  94                                dentry->d_fsdata = (void *)(unsigned long)cnid;
  95                                linkid =
  96                                        be32_to_cpu(entry.file.permissions.dev);
  97                                str.len = sprintf(name, "iNode%d", linkid);
  98                                str.name = name;
  99                                hfsplus_cat_build_key(sb, fd.search_key,
 100                                        HFSPLUS_SB(sb)->hidden_dir->i_ino,
 101                                        &str);
 102                                goto again;
 103                        }
 104                } else if (!dentry->d_fsdata)
 105                        dentry->d_fsdata = (void *)(unsigned long)cnid;
 106        } else {
 107                pr_err("invalid catalog entry type in lookup\n");
 108                err = -EIO;
 109                goto fail;
 110        }
 111        hfs_find_exit(&fd);
 112        inode = hfsplus_iget(dir->i_sb, cnid);
 113        if (IS_ERR(inode))
 114                return ERR_CAST(inode);
 115        if (S_ISREG(inode->i_mode))
 116                HFSPLUS_I(inode)->linkid = linkid;
 117out:
 118        d_add(dentry, inode);
 119        return NULL;
 120fail:
 121        hfs_find_exit(&fd);
 122        return ERR_PTR(err);
 123}
 124
 125static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
 126{
 127        struct inode *inode = file_inode(file);
 128        struct super_block *sb = inode->i_sb;
 129        int len, err;
 130        char strbuf[HFSPLUS_MAX_STRLEN + 1];
 131        hfsplus_cat_entry entry;
 132        struct hfs_find_data fd;
 133        struct hfsplus_readdir_data *rd;
 134        u16 type;
 135
 136        if (file->f_pos >= inode->i_size)
 137                return 0;
 138
 139        err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
 140        if (err)
 141                return err;
 142        hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
 143        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 144        if (err)
 145                goto out;
 146
 147        if (ctx->pos == 0) {
 148                /* This is completely artificial... */
 149                if (!dir_emit_dot(file, ctx))
 150                        goto out;
 151                ctx->pos = 1;
 152        }
 153        if (ctx->pos == 1) {
 154                if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
 155                        err = -EIO;
 156                        goto out;
 157                }
 158
 159                hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
 160                        fd.entrylength);
 161                if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
 162                        pr_err("bad catalog folder thread\n");
 163                        err = -EIO;
 164                        goto out;
 165                }
 166                if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
 167                        pr_err("truncated catalog thread\n");
 168                        err = -EIO;
 169                        goto out;
 170                }
 171                if (!dir_emit(ctx, "..", 2,
 172                            be32_to_cpu(entry.thread.parentID), DT_DIR))
 173                        goto out;
 174                ctx->pos = 2;
 175        }
 176        if (ctx->pos >= inode->i_size)
 177                goto out;
 178        err = hfs_brec_goto(&fd, ctx->pos - 1);
 179        if (err)
 180                goto out;
 181        for (;;) {
 182                if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
 183                        pr_err("walked past end of dir\n");
 184                        err = -EIO;
 185                        goto out;
 186                }
 187
 188                if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
 189                        err = -EIO;
 190                        goto out;
 191                }
 192
 193                hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
 194                        fd.entrylength);
 195                type = be16_to_cpu(entry.type);
 196                len = HFSPLUS_MAX_STRLEN;
 197                err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
 198                if (err)
 199                        goto out;
 200                if (type == HFSPLUS_FOLDER) {
 201                        if (fd.entrylength <
 202                                        sizeof(struct hfsplus_cat_folder)) {
 203                                pr_err("small dir entry\n");
 204                                err = -EIO;
 205                                goto out;
 206                        }
 207                        if (HFSPLUS_SB(sb)->hidden_dir &&
 208                            HFSPLUS_SB(sb)->hidden_dir->i_ino ==
 209                                        be32_to_cpu(entry.folder.id))
 210                                goto next;
 211                        if (!dir_emit(ctx, strbuf, len,
 212                                    be32_to_cpu(entry.folder.id), DT_DIR))
 213                                break;
 214                } else if (type == HFSPLUS_FILE) {
 215                        if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
 216                                pr_err("small file entry\n");
 217                                err = -EIO;
 218                                goto out;
 219                        }
 220                        if (!dir_emit(ctx, strbuf, len,
 221                                    be32_to_cpu(entry.file.id), DT_REG))
 222                                break;
 223                } else {
 224                        pr_err("bad catalog entry type\n");
 225                        err = -EIO;
 226                        goto out;
 227                }
 228next:
 229                ctx->pos++;
 230                if (ctx->pos >= inode->i_size)
 231                        goto out;
 232                err = hfs_brec_goto(&fd, 1);
 233                if (err)
 234                        goto out;
 235        }
 236        rd = file->private_data;
 237        if (!rd) {
 238                rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
 239                if (!rd) {
 240                        err = -ENOMEM;
 241                        goto out;
 242                }
 243                file->private_data = rd;
 244                rd->file = file;
 245                list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
 246        }
 247        memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
 248out:
 249        hfs_find_exit(&fd);
 250        return err;
 251}
 252
 253static int hfsplus_dir_release(struct inode *inode, struct file *file)
 254{
 255        struct hfsplus_readdir_data *rd = file->private_data;
 256        if (rd) {
 257                mutex_lock(&inode->i_mutex);
 258                list_del(&rd->list);
 259                mutex_unlock(&inode->i_mutex);
 260                kfree(rd);
 261        }
 262        return 0;
 263}
 264
 265static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
 266                        struct dentry *dst_dentry)
 267{
 268        struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
 269        struct inode *inode = src_dentry->d_inode;
 270        struct inode *src_dir = src_dentry->d_parent->d_inode;
 271        struct qstr str;
 272        char name[32];
 273        u32 cnid, id;
 274        int res;
 275
 276        if (HFSPLUS_IS_RSRC(inode))
 277                return -EPERM;
 278        if (!S_ISREG(inode->i_mode))
 279                return -EPERM;
 280
 281        mutex_lock(&sbi->vh_mutex);
 282        if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
 283                for (;;) {
 284                        get_random_bytes(&id, sizeof(cnid));
 285                        id &= 0x3fffffff;
 286                        str.name = name;
 287                        str.len = sprintf(name, "iNode%d", id);
 288                        res = hfsplus_rename_cat(inode->i_ino,
 289                                                 src_dir, &src_dentry->d_name,
 290                                                 sbi->hidden_dir, &str);
 291                        if (!res)
 292                                break;
 293                        if (res != -EEXIST)
 294                                goto out;
 295                }
 296                HFSPLUS_I(inode)->linkid = id;
 297                cnid = sbi->next_cnid++;
 298                src_dentry->d_fsdata = (void *)(unsigned long)cnid;
 299                res = hfsplus_create_cat(cnid, src_dir,
 300                        &src_dentry->d_name, inode);
 301                if (res)
 302                        /* panic? */
 303                        goto out;
 304                sbi->file_count++;
 305        }
 306        cnid = sbi->next_cnid++;
 307        res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
 308        if (res)
 309                goto out;
 310
 311        inc_nlink(inode);
 312        hfsplus_instantiate(dst_dentry, inode, cnid);
 313        ihold(inode);
 314        inode->i_ctime = CURRENT_TIME_SEC;
 315        mark_inode_dirty(inode);
 316        sbi->file_count++;
 317        hfsplus_mark_mdb_dirty(dst_dir->i_sb);
 318out:
 319        mutex_unlock(&sbi->vh_mutex);
 320        return res;
 321}
 322
 323static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
 324{
 325        struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 326        struct inode *inode = dentry->d_inode;
 327        struct qstr str;
 328        char name[32];
 329        u32 cnid;
 330        int res;
 331
 332        if (HFSPLUS_IS_RSRC(inode))
 333                return -EPERM;
 334
 335        mutex_lock(&sbi->vh_mutex);
 336        cnid = (u32)(unsigned long)dentry->d_fsdata;
 337        if (inode->i_ino == cnid &&
 338            atomic_read(&HFSPLUS_I(inode)->opencnt)) {
 339                str.name = name;
 340                str.len = sprintf(name, "temp%lu", inode->i_ino);
 341                res = hfsplus_rename_cat(inode->i_ino,
 342                                         dir, &dentry->d_name,
 343                                         sbi->hidden_dir, &str);
 344                if (!res) {
 345                        inode->i_flags |= S_DEAD;
 346                        drop_nlink(inode);
 347                }
 348                goto out;
 349        }
 350        res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
 351        if (res)
 352                goto out;
 353
 354        if (inode->i_nlink > 0)
 355                drop_nlink(inode);
 356        if (inode->i_ino == cnid)
 357                clear_nlink(inode);
 358        if (!inode->i_nlink) {
 359                if (inode->i_ino != cnid) {
 360                        sbi->file_count--;
 361                        if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
 362                                res = hfsplus_delete_cat(inode->i_ino,
 363                                                         sbi->hidden_dir,
 364                                                         NULL);
 365                                if (!res)
 366                                        hfsplus_delete_inode(inode);
 367                        } else
 368                                inode->i_flags |= S_DEAD;
 369                } else
 370                        hfsplus_delete_inode(inode);
 371        } else
 372                sbi->file_count--;
 373        inode->i_ctime = CURRENT_TIME_SEC;
 374        mark_inode_dirty(inode);
 375out:
 376        mutex_unlock(&sbi->vh_mutex);
 377        return res;
 378}
 379
 380static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
 381{
 382        struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 383        struct inode *inode = dentry->d_inode;
 384        int res;
 385
 386        if (inode->i_size != 2)
 387                return -ENOTEMPTY;
 388
 389        mutex_lock(&sbi->vh_mutex);
 390        res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 391        if (res)
 392                goto out;
 393        clear_nlink(inode);
 394        inode->i_ctime = CURRENT_TIME_SEC;
 395        hfsplus_delete_inode(inode);
 396        mark_inode_dirty(inode);
 397out:
 398        mutex_unlock(&sbi->vh_mutex);
 399        return res;
 400}
 401
 402static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
 403                           const char *symname)
 404{
 405        struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 406        struct inode *inode;
 407        int res = -ENOSPC;
 408
 409        mutex_lock(&sbi->vh_mutex);
 410        inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
 411        if (!inode)
 412                goto out;
 413
 414        res = page_symlink(inode, symname, strlen(symname) + 1);
 415        if (res)
 416                goto out_err;
 417
 418        res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
 419        if (res)
 420                goto out_err;
 421
 422        res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
 423        if (res == -EOPNOTSUPP)
 424                res = 0; /* Operation is not supported. */
 425        else if (res) {
 426                /* Try to delete anyway without error analysis. */
 427                hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 428                goto out_err;
 429        }
 430
 431        hfsplus_instantiate(dentry, inode, inode->i_ino);
 432        mark_inode_dirty(inode);
 433        goto out;
 434
 435out_err:
 436        clear_nlink(inode);
 437        hfsplus_delete_inode(inode);
 438        iput(inode);
 439out:
 440        mutex_unlock(&sbi->vh_mutex);
 441        return res;
 442}
 443
 444static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
 445                         umode_t mode, dev_t rdev)
 446{
 447        struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 448        struct inode *inode;
 449        int res = -ENOSPC;
 450
 451        mutex_lock(&sbi->vh_mutex);
 452        inode = hfsplus_new_inode(dir->i_sb, mode);
 453        if (!inode)
 454                goto out;
 455
 456        if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
 457                init_special_inode(inode, mode, rdev);
 458
 459        res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
 460        if (res)
 461                goto failed_mknod;
 462
 463        res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
 464        if (res == -EOPNOTSUPP)
 465                res = 0; /* Operation is not supported. */
 466        else if (res) {
 467                /* Try to delete anyway without error analysis. */
 468                hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 469                goto failed_mknod;
 470        }
 471
 472        hfsplus_instantiate(dentry, inode, inode->i_ino);
 473        mark_inode_dirty(inode);
 474        goto out;
 475
 476failed_mknod:
 477        clear_nlink(inode);
 478        hfsplus_delete_inode(inode);
 479        iput(inode);
 480out:
 481        mutex_unlock(&sbi->vh_mutex);
 482        return res;
 483}
 484
 485static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 486                          bool excl)
 487{
 488        return hfsplus_mknod(dir, dentry, mode, 0);
 489}
 490
 491static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 492{
 493        return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
 494}
 495
 496static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
 497                          struct inode *new_dir, struct dentry *new_dentry)
 498{
 499        int res;
 500
 501        /* Unlink destination if it already exists */
 502        if (new_dentry->d_inode) {
 503                if (S_ISDIR(new_dentry->d_inode->i_mode))
 504                        res = hfsplus_rmdir(new_dir, new_dentry);
 505                else
 506                        res = hfsplus_unlink(new_dir, new_dentry);
 507                if (res)
 508                        return res;
 509        }
 510
 511        res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
 512                                 old_dir, &old_dentry->d_name,
 513                                 new_dir, &new_dentry->d_name);
 514        if (!res)
 515                new_dentry->d_fsdata = old_dentry->d_fsdata;
 516        return res;
 517}
 518
 519const struct inode_operations hfsplus_dir_inode_operations = {
 520        .lookup                 = hfsplus_lookup,
 521        .create                 = hfsplus_create,
 522        .link                   = hfsplus_link,
 523        .unlink                 = hfsplus_unlink,
 524        .mkdir                  = hfsplus_mkdir,
 525        .rmdir                  = hfsplus_rmdir,
 526        .symlink                = hfsplus_symlink,
 527        .mknod                  = hfsplus_mknod,
 528        .rename                 = hfsplus_rename,
 529        .setxattr               = generic_setxattr,
 530        .getxattr               = generic_getxattr,
 531        .listxattr              = hfsplus_listxattr,
 532        .removexattr            = hfsplus_removexattr,
 533#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
 534        .get_acl                = hfsplus_get_posix_acl,
 535#endif
 536};
 537
 538const struct file_operations hfsplus_dir_operations = {
 539        .fsync          = hfsplus_file_fsync,
 540        .read           = generic_read_dir,
 541        .iterate        = hfsplus_readdir,
 542        .unlocked_ioctl = hfsplus_ioctl,
 543        .llseek         = generic_file_llseek,
 544        .release        = hfsplus_dir_release,
 545};
 546
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.