]>
Commit | Line | Data |
---|---|---|
21b6633d SM |
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 | ||
ea3983ac NJ |
17 | struct fat_fid { |
18 | u32 i_gen; | |
19 | u32 i_pos_low; | |
20 | u16 i_pos_hi; | |
21 | u16 parent_i_pos_hi; | |
22 | u32 parent_i_pos_low; | |
23 | u32 parent_i_gen; | |
24 | }; | |
25 | ||
26 | #define FAT_FID_SIZE_WITHOUT_PARENT 3 | |
27 | #define FAT_FID_SIZE_WITH_PARENT (sizeof(struct fat_fid)/sizeof(u32)) | |
28 | ||
7669e8fb SM |
29 | /** |
30 | * Look up a directory inode given its starting cluster. | |
21b6633d | 31 | */ |
7669e8fb | 32 | static struct inode *fat_dget(struct super_block *sb, int i_logstart) |
21b6633d | 33 | { |
7669e8fb SM |
34 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
35 | struct hlist_head *head; | |
7669e8fb SM |
36 | struct msdos_inode_info *i; |
37 | struct inode *inode = NULL; | |
21b6633d | 38 | |
7669e8fb SM |
39 | head = sbi->dir_hashtable + fat_dir_hash(i_logstart); |
40 | spin_lock(&sbi->dir_hash_lock); | |
b67bfe0d | 41 | hlist_for_each_entry(i, head, i_dir_hash) { |
7669e8fb SM |
42 | BUG_ON(i->vfs_inode.i_sb != sb); |
43 | if (i->i_logstart != i_logstart) | |
44 | continue; | |
45 | inode = igrab(&i->vfs_inode); | |
46 | if (inode) | |
47 | break; | |
21b6633d | 48 | } |
7669e8fb SM |
49 | spin_unlock(&sbi->dir_hash_lock); |
50 | return inode; | |
21b6633d SM |
51 | } |
52 | ||
ea3983ac NJ |
53 | static struct inode *__fat_nfs_get_inode(struct super_block *sb, |
54 | u64 ino, u32 generation, loff_t i_pos) | |
21b6633d | 55 | { |
7669e8fb SM |
56 | struct inode *inode; |
57 | ||
58 | if ((ino < MSDOS_ROOT_INO) || (ino == MSDOS_FSINFO_INO)) | |
59 | return NULL; | |
60 | ||
61 | inode = ilookup(sb, ino); | |
62 | if (inode && generation && (inode->i_generation != generation)) { | |
63 | iput(inode); | |
64 | inode = NULL; | |
65 | } | |
66 | ||
67 | return inode; | |
21b6633d SM |
68 | } |
69 | ||
ea3983ac NJ |
70 | static struct inode *fat_nfs_get_inode(struct super_block *sb, |
71 | u64 ino, u32 generation) | |
72 | { | |
73 | ||
74 | return __fat_nfs_get_inode(sb, ino, generation, 0); | |
75 | } | |
76 | ||
77 | static int | |
78 | fat_encode_fh_nostale(struct inode *inode, __u32 *fh, int *lenp, | |
79 | struct inode *parent) | |
80 | { | |
81 | int len = *lenp; | |
82 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | |
83 | struct fat_fid *fid = (struct fat_fid *) fh; | |
84 | loff_t i_pos; | |
85 | int type = FILEID_FAT_WITHOUT_PARENT; | |
86 | ||
87 | if (parent) { | |
88 | if (len < FAT_FID_SIZE_WITH_PARENT) { | |
89 | *lenp = FAT_FID_SIZE_WITH_PARENT; | |
90 | return FILEID_INVALID; | |
91 | } | |
92 | } else { | |
93 | if (len < FAT_FID_SIZE_WITHOUT_PARENT) { | |
94 | *lenp = FAT_FID_SIZE_WITHOUT_PARENT; | |
95 | return FILEID_INVALID; | |
96 | } | |
97 | } | |
98 | ||
99 | i_pos = fat_i_pos_read(sbi, inode); | |
100 | *lenp = FAT_FID_SIZE_WITHOUT_PARENT; | |
101 | fid->i_gen = inode->i_generation; | |
102 | fid->i_pos_low = i_pos & 0xFFFFFFFF; | |
103 | fid->i_pos_hi = (i_pos >> 32) & 0xFFFF; | |
104 | if (parent) { | |
105 | i_pos = fat_i_pos_read(sbi, parent); | |
106 | fid->parent_i_pos_hi = (i_pos >> 32) & 0xFFFF; | |
107 | fid->parent_i_pos_low = i_pos & 0xFFFFFFFF; | |
108 | fid->parent_i_gen = parent->i_generation; | |
109 | type = FILEID_FAT_WITH_PARENT; | |
110 | *lenp = FAT_FID_SIZE_WITH_PARENT; | |
111 | } | |
112 | ||
113 | return type; | |
114 | } | |
115 | ||
21b6633d SM |
116 | /** |
117 | * Map a NFS file handle to a corresponding dentry. | |
118 | * The dentry may or may not be connected to the filesystem root. | |
119 | */ | |
ea3983ac | 120 | static struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, |
21b6633d SM |
121 | int fh_len, int fh_type) |
122 | { | |
7669e8fb SM |
123 | return generic_fh_to_dentry(sb, fid, fh_len, fh_type, |
124 | fat_nfs_get_inode); | |
125 | } | |
21b6633d | 126 | |
ea3983ac NJ |
127 | static struct dentry *fat_fh_to_dentry_nostale(struct super_block *sb, |
128 | struct fid *fh, int fh_len, | |
129 | int fh_type) | |
130 | { | |
131 | struct inode *inode = NULL; | |
132 | struct fat_fid *fid = (struct fat_fid *)fh; | |
133 | loff_t i_pos; | |
134 | ||
135 | switch (fh_type) { | |
136 | case FILEID_FAT_WITHOUT_PARENT: | |
137 | if (fh_len < FAT_FID_SIZE_WITHOUT_PARENT) | |
138 | return NULL; | |
139 | break; | |
140 | case FILEID_FAT_WITH_PARENT: | |
141 | if (fh_len < FAT_FID_SIZE_WITH_PARENT) | |
142 | return NULL; | |
143 | break; | |
144 | default: | |
145 | return NULL; | |
146 | } | |
147 | i_pos = fid->i_pos_hi; | |
148 | i_pos = (i_pos << 32) | (fid->i_pos_low); | |
149 | inode = __fat_nfs_get_inode(sb, 0, fid->i_gen, i_pos); | |
150 | ||
151 | return d_obtain_alias(inode); | |
152 | } | |
153 | ||
7669e8fb SM |
154 | /* |
155 | * Find the parent for a file specified by NFS handle. | |
156 | * This requires that the handle contain the i_ino of the parent. | |
157 | */ | |
ea3983ac | 158 | static struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid, |
7669e8fb SM |
159 | int fh_len, int fh_type) |
160 | { | |
161 | return generic_fh_to_parent(sb, fid, fh_len, fh_type, | |
162 | fat_nfs_get_inode); | |
21b6633d SM |
163 | } |
164 | ||
ea3983ac NJ |
165 | static struct dentry *fat_fh_to_parent_nostale(struct super_block *sb, |
166 | struct fid *fh, int fh_len, | |
167 | int fh_type) | |
168 | { | |
169 | struct inode *inode = NULL; | |
170 | struct fat_fid *fid = (struct fat_fid *)fh; | |
171 | loff_t i_pos; | |
172 | ||
173 | if (fh_len < FAT_FID_SIZE_WITH_PARENT) | |
174 | return NULL; | |
175 | ||
176 | switch (fh_type) { | |
177 | case FILEID_FAT_WITH_PARENT: | |
178 | i_pos = fid->parent_i_pos_hi; | |
179 | i_pos = (i_pos << 32) | (fid->parent_i_pos_low); | |
180 | inode = __fat_nfs_get_inode(sb, 0, fid->parent_i_gen, i_pos); | |
181 | break; | |
182 | } | |
183 | ||
184 | return d_obtain_alias(inode); | |
185 | } | |
186 | ||
21b6633d SM |
187 | /* |
188 | * Find the parent for a directory that is not currently connected to | |
189 | * the filesystem root. | |
190 | * | |
191 | * On entry, the caller holds child_dir->d_inode->i_mutex. | |
192 | */ | |
ea3983ac | 193 | static struct dentry *fat_get_parent(struct dentry *child_dir) |
21b6633d SM |
194 | { |
195 | struct super_block *sb = child_dir->d_sb; | |
196 | struct buffer_head *bh = NULL; | |
197 | struct msdos_dir_entry *de; | |
7669e8fb | 198 | struct inode *parent_inode = NULL; |
21b6633d | 199 | |
7669e8fb SM |
200 | if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) { |
201 | int parent_logstart = fat_get_start(MSDOS_SB(sb), de); | |
202 | parent_inode = fat_dget(sb, parent_logstart); | |
21b6633d | 203 | } |
21b6633d | 204 | brelse(bh); |
21b6633d | 205 | |
7669e8fb | 206 | return d_obtain_alias(parent_inode); |
21b6633d | 207 | } |
ea3983ac NJ |
208 | |
209 | const struct export_operations fat_export_ops = { | |
210 | .fh_to_dentry = fat_fh_to_dentry, | |
211 | .fh_to_parent = fat_fh_to_parent, | |
212 | .get_parent = fat_get_parent, | |
213 | }; | |
214 | ||
215 | const struct export_operations fat_export_ops_nostale = { | |
216 | .encode_fh = fat_encode_fh_nostale, | |
217 | .fh_to_dentry = fat_fh_to_dentry_nostale, | |
218 | .fh_to_parent = fat_fh_to_parent_nostale, | |
219 | .get_parent = fat_get_parent, | |
220 | }; |