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