Steven J. Magnani | 21b6633d5 | 2012-10-04 17:14:44 -0700 | [diff] [blame^] | 1 | /* fs/fat/nfs.c |
| 2 | * |
| 3 | * This software is licensed under the terms of the GNU General Public |
| 4 | * License version 2, as published by the Free Software Foundation, and |
| 5 | * may be copied, distributed, and modified under those terms. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | * |
| 12 | */ |
| 13 | |
| 14 | #include <linux/exportfs.h> |
| 15 | #include "fat.h" |
| 16 | |
| 17 | /* |
| 18 | * a FAT file handle with fhtype 3 is |
| 19 | * 0/ i_ino - for fast, reliable lookup if still in the cache |
| 20 | * 1/ i_generation - to see if i_ino is still valid |
| 21 | * bit 0 == 0 iff directory |
| 22 | * 2/ i_pos(8-39) - if ino has changed, but still in cache |
| 23 | * 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos |
| 24 | * 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc |
| 25 | * |
| 26 | * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum |
| 27 | * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits |
| 28 | * of i_logstart is used to store the directory entry offset. |
| 29 | */ |
| 30 | |
| 31 | int |
| 32 | fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent) |
| 33 | { |
| 34 | int len = *lenp; |
| 35 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); |
| 36 | loff_t i_pos; |
| 37 | |
| 38 | if (len < 5) { |
| 39 | *lenp = 5; |
| 40 | return 255; /* no room */ |
| 41 | } |
| 42 | |
| 43 | i_pos = fat_i_pos_read(sbi, inode); |
| 44 | *lenp = 5; |
| 45 | fh[0] = inode->i_ino; |
| 46 | fh[1] = inode->i_generation; |
| 47 | fh[2] = i_pos >> 8; |
| 48 | fh[3] = ((i_pos & 0xf0) << 24) | MSDOS_I(inode)->i_logstart; |
| 49 | fh[4] = (i_pos & 0x0f) << 28; |
| 50 | if (parent) |
| 51 | fh[4] |= MSDOS_I(parent)->i_logstart; |
| 52 | return 3; |
| 53 | } |
| 54 | |
| 55 | static int fat_is_valid_fh(int fh_len, int fh_type) |
| 56 | { |
| 57 | return ((fh_len >= 5) && (fh_type == 3)); |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Map a NFS file handle to a corresponding dentry. |
| 62 | * The dentry may or may not be connected to the filesystem root. |
| 63 | */ |
| 64 | struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, |
| 65 | int fh_len, int fh_type) |
| 66 | { |
| 67 | struct inode *inode = NULL; |
| 68 | u32 *fh = fid->raw; |
| 69 | loff_t i_pos; |
| 70 | unsigned long i_ino; |
| 71 | __u32 i_generation; |
| 72 | int i_logstart; |
| 73 | |
| 74 | if (!fat_is_valid_fh(fh_len, fh_type)) |
| 75 | return NULL; |
| 76 | |
| 77 | i_ino = fh[0]; |
| 78 | i_generation = fh[1]; |
| 79 | i_logstart = fh[3] & 0x0fffffff; |
| 80 | |
| 81 | /* Try i_ino lookup first - fastest and most reliable */ |
| 82 | inode = ilookup(sb, i_ino); |
| 83 | if (inode && (inode->i_generation != i_generation)) { |
| 84 | iput(inode); |
| 85 | inode = NULL; |
| 86 | } |
| 87 | if (!inode) { |
| 88 | i_pos = (loff_t)fh[2] << 8; |
| 89 | i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28); |
| 90 | |
| 91 | /* try 2 - see if i_pos is in F-d-c |
| 92 | * require i_logstart to be the same |
| 93 | * Will fail if you truncate and then re-write |
| 94 | */ |
| 95 | |
| 96 | inode = fat_iget(sb, i_pos); |
| 97 | if (inode && MSDOS_I(inode)->i_logstart != i_logstart) { |
| 98 | iput(inode); |
| 99 | inode = NULL; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /* |
| 104 | * For now, do nothing if the inode is not found. |
| 105 | * |
| 106 | * What we could do is: |
| 107 | * |
| 108 | * - follow the file starting at fh[4], and record the ".." entry, |
| 109 | * and the name of the fh[2] entry. |
| 110 | * - then follow the ".." file finding the next step up. |
| 111 | * |
| 112 | * This way we build a path to the root of the tree. If this works, we |
| 113 | * lookup the path and so get this inode into the cache. Finally try |
| 114 | * the fat_iget lookup again. If that fails, then we are totally out |
| 115 | * of luck. But all that is for another day |
| 116 | */ |
| 117 | return d_obtain_alias(inode); |
| 118 | } |
| 119 | |
| 120 | /* |
| 121 | * Find the parent for a directory that is not currently connected to |
| 122 | * the filesystem root. |
| 123 | * |
| 124 | * On entry, the caller holds child_dir->d_inode->i_mutex. |
| 125 | */ |
| 126 | struct dentry *fat_get_parent(struct dentry *child_dir) |
| 127 | { |
| 128 | struct super_block *sb = child_dir->d_sb; |
| 129 | struct buffer_head *bh = NULL; |
| 130 | struct msdos_dir_entry *de; |
| 131 | loff_t i_pos; |
| 132 | struct dentry *parent; |
| 133 | struct inode *inode; |
| 134 | int err; |
| 135 | |
| 136 | lock_super(sb); |
| 137 | |
| 138 | err = fat_get_dotdot_entry(child_dir->d_inode, &bh, &de, &i_pos); |
| 139 | if (err) { |
| 140 | parent = ERR_PTR(err); |
| 141 | goto out; |
| 142 | } |
| 143 | inode = fat_build_inode(sb, de, i_pos); |
| 144 | |
| 145 | parent = d_obtain_alias(inode); |
| 146 | out: |
| 147 | brelse(bh); |
| 148 | unlock_super(sb); |
| 149 | |
| 150 | return parent; |
| 151 | } |