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