linux/fs/isofs/export.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * fs/isofs/export.c
   4 *
   5 *  (C) 2004  Paul Serice - The new inode scheme requires switching
   6 *                          from iget() to iget5_locked() which means
   7 *                          the NFS export operations have to be hand
   8 *                          coded because the default routines rely on
   9 *                          iget().
  10 *
  11 * The following files are helpful:
  12 *
  13 *     Documentation/filesystems/nfs/Exporting
  14 *     fs/exportfs/expfs.c.
  15 */
  16
  17#include "isofs.h"
  18
  19static struct dentry *
  20isofs_export_iget(struct super_block *sb,
  21                  unsigned long block,
  22                  unsigned long offset,
  23                  __u32 generation)
  24{
  25        struct inode *inode;
  26
  27        if (block == 0)
  28                return ERR_PTR(-ESTALE);
  29        inode = isofs_iget(sb, block, offset);
  30        if (IS_ERR(inode))
  31                return ERR_CAST(inode);
  32        if (generation && inode->i_generation != generation) {
  33                iput(inode);
  34                return ERR_PTR(-ESTALE);
  35        }
  36        return d_obtain_alias(inode);
  37}
  38
  39/* This function is surprisingly simple.  The trick is understanding
  40 * that "child" is always a directory. So, to find its parent, you
  41 * simply need to find its ".." entry, normalize its block and offset,
  42 * and return the underlying inode.  See the comments for
  43 * isofs_normalize_block_and_offset(). */
  44static struct dentry *isofs_export_get_parent(struct dentry *child)
  45{
  46        unsigned long parent_block = 0;
  47        unsigned long parent_offset = 0;
  48        struct inode *child_inode = d_inode(child);
  49        struct iso_inode_info *e_child_inode = ISOFS_I(child_inode);
  50        struct iso_directory_record *de = NULL;
  51        struct buffer_head * bh = NULL;
  52        struct dentry *rv = NULL;
  53
  54        /* "child" must always be a directory. */
  55        if (!S_ISDIR(child_inode->i_mode)) {
  56                printk(KERN_ERR "isofs: isofs_export_get_parent(): "
  57                       "child is not a directory!\n");
  58                rv = ERR_PTR(-EACCES);
  59                goto out;
  60        }
  61
  62        /* It is an invariant that the directory offset is zero.  If
  63         * it is not zero, it means the directory failed to be
  64         * normalized for some reason. */
  65        if (e_child_inode->i_iget5_offset != 0) {
  66                printk(KERN_ERR "isofs: isofs_export_get_parent(): "
  67                       "child directory not normalized!\n");
  68                rv = ERR_PTR(-EACCES);
  69                goto out;
  70        }
  71
  72        /* The child inode has been normalized such that its
  73         * i_iget5_block value points to the "." entry.  Fortunately,
  74         * the ".." entry is located in the same block. */
  75        parent_block = e_child_inode->i_iget5_block;
  76
  77        /* Get the block in question. */
  78        bh = sb_bread(child_inode->i_sb, parent_block);
  79        if (bh == NULL) {
  80                rv = ERR_PTR(-EACCES);
  81                goto out;
  82        }
  83
  84        /* This is the "." entry. */
  85        de = (struct iso_directory_record*)bh->b_data;
  86
  87        /* The ".." entry is always the second entry. */
  88        parent_offset = (unsigned long)isonum_711(de->length);
  89        de = (struct iso_directory_record*)(bh->b_data + parent_offset);
  90
  91        /* Verify it is in fact the ".." entry. */
  92        if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) {
  93                printk(KERN_ERR "isofs: Unable to find the \"..\" "
  94                       "directory for NFS.\n");
  95                rv = ERR_PTR(-EACCES);
  96                goto out;
  97        }
  98
  99        /* Normalize */
 100        isofs_normalize_block_and_offset(de, &parent_block, &parent_offset);
 101
 102        rv = d_obtain_alias(isofs_iget(child_inode->i_sb, parent_block,
 103                                     parent_offset));
 104 out:
 105        if (bh)
 106                brelse(bh);
 107        return rv;
 108}
 109
 110static int
 111isofs_export_encode_fh(struct inode *inode,
 112                       __u32 *fh32,
 113                       int *max_len,
 114                       struct inode *parent)
 115{
 116        struct iso_inode_info * ei = ISOFS_I(inode);
 117        int len = *max_len;
 118        int type = 1;
 119        __u16 *fh16 = (__u16*)fh32;
 120
 121        /*
 122         * WARNING: max_len is 5 for NFSv2.  Because of this
 123         * limitation, we use the lower 16 bits of fh32[1] to hold the
 124         * offset of the inode and the upper 16 bits of fh32[1] to
 125         * hold the offset of the parent.
 126         */
 127        if (parent && (len < 5)) {
 128                *max_len = 5;
 129                return FILEID_INVALID;
 130        } else if (len < 3) {
 131                *max_len = 3;
 132                return FILEID_INVALID;
 133        }
 134
 135        len = 3;
 136        fh32[0] = ei->i_iget5_block;
 137        fh16[2] = (__u16)ei->i_iget5_offset;  /* fh16 [sic] */
 138        fh16[3] = 0;  /* avoid leaking uninitialized data */
 139        fh32[2] = inode->i_generation;
 140        if (parent) {
 141                struct iso_inode_info *eparent;
 142                eparent = ISOFS_I(parent);
 143                fh32[3] = eparent->i_iget5_block;
 144                fh16[3] = (__u16)eparent->i_iget5_offset;  /* fh16 [sic] */
 145                fh32[4] = parent->i_generation;
 146                len = 5;
 147                type = 2;
 148        }
 149        *max_len = len;
 150        return type;
 151}
 152
 153struct isofs_fid {
 154        u32 block;
 155        u16 offset;
 156        u16 parent_offset;
 157        u32 generation;
 158        u32 parent_block;
 159        u32 parent_generation;
 160};
 161
 162static struct dentry *isofs_fh_to_dentry(struct super_block *sb,
 163        struct fid *fid, int fh_len, int fh_type)
 164{
 165        struct isofs_fid *ifid = (struct isofs_fid *)fid;
 166
 167        if (fh_len < 3 || fh_type > 2)
 168                return NULL;
 169
 170        return isofs_export_iget(sb, ifid->block, ifid->offset,
 171                        ifid->generation);
 172}
 173
 174static struct dentry *isofs_fh_to_parent(struct super_block *sb,
 175                struct fid *fid, int fh_len, int fh_type)
 176{
 177        struct isofs_fid *ifid = (struct isofs_fid *)fid;
 178
 179        if (fh_len < 2 || fh_type != 2)
 180                return NULL;
 181
 182        return isofs_export_iget(sb,
 183                        fh_len > 2 ? ifid->parent_block : 0,
 184                        ifid->parent_offset,
 185                        fh_len > 4 ? ifid->parent_generation : 0);
 186}
 187
 188const struct export_operations isofs_export_ops = {
 189        .encode_fh      = isofs_export_encode_fh,
 190        .fh_to_dentry   = isofs_fh_to_dentry,
 191        .fh_to_parent   = isofs_fh_to_parent,
 192        .get_parent     = isofs_export_get_parent,
 193};
 194