diff options
-rw-r--r-- | fs/udf/namei.c | 140 | ||||
-rw-r--r-- | fs/udf/super.c | 1 | ||||
-rw-r--r-- | fs/udf/udfdecl.h | 1 | ||||
-rw-r--r-- | include/linux/exportfs.h | 21 |
4 files changed, 161 insertions, 2 deletions
diff --git a/fs/udf/namei.c b/fs/udf/namei.c index ba5537d4bc15..47a6589e10b5 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/buffer_head.h> | 32 | #include <linux/buffer_head.h> |
33 | #include <linux/sched.h> | 33 | #include <linux/sched.h> |
34 | #include <linux/crc-itu-t.h> | 34 | #include <linux/crc-itu-t.h> |
35 | #include <linux/exportfs.h> | ||
35 | 36 | ||
36 | static inline int udf_match(int len1, const char *name1, int len2, | 37 | static inline int udf_match(int len1, const char *name1, int len2, |
37 | const char *name2) | 38 | const char *name2) |
@@ -158,6 +159,8 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, | |||
158 | sector_t offset; | 159 | sector_t offset; |
159 | struct extent_position epos = {}; | 160 | struct extent_position epos = {}; |
160 | struct udf_inode_info *dinfo = UDF_I(dir); | 161 | struct udf_inode_info *dinfo = UDF_I(dir); |
162 | int isdotdot = dentry->d_name.len == 2 && | ||
163 | dentry->d_name.name[0] == '.' && dentry->d_name.name[1] == '.'; | ||
161 | 164 | ||
162 | size = udf_ext0_offset(dir) + dir->i_size; | 165 | size = udf_ext0_offset(dir) + dir->i_size; |
163 | f_pos = udf_ext0_offset(dir); | 166 | f_pos = udf_ext0_offset(dir); |
@@ -225,6 +228,12 @@ static struct fileIdentDesc *udf_find_entry(struct inode *dir, | |||
225 | continue; | 228 | continue; |
226 | } | 229 | } |
227 | 230 | ||
231 | if ((cfi->fileCharacteristics & FID_FILE_CHAR_PARENT) && | ||
232 | isdotdot) { | ||
233 | brelse(epos.bh); | ||
234 | return fi; | ||
235 | } | ||
236 | |||
228 | if (!lfi) | 237 | if (!lfi) |
229 | continue; | 238 | continue; |
230 | 239 | ||
@@ -286,9 +295,8 @@ static struct dentry *udf_lookup(struct inode *dir, struct dentry *dentry, | |||
286 | } | 295 | } |
287 | } | 296 | } |
288 | unlock_kernel(); | 297 | unlock_kernel(); |
289 | d_add(dentry, inode); | ||
290 | 298 | ||
291 | return NULL; | 299 | return d_splice_alias(inode, dentry); |
292 | } | 300 | } |
293 | 301 | ||
294 | static struct fileIdentDesc *udf_add_entry(struct inode *dir, | 302 | static struct fileIdentDesc *udf_add_entry(struct inode *dir, |
@@ -1232,6 +1240,134 @@ end_rename: | |||
1232 | return retval; | 1240 | return retval; |
1233 | } | 1241 | } |
1234 | 1242 | ||
1243 | static struct dentry *udf_get_parent(struct dentry *child) | ||
1244 | { | ||
1245 | struct dentry *parent; | ||
1246 | struct inode *inode = NULL; | ||
1247 | struct dentry dotdot; | ||
1248 | struct fileIdentDesc cfi; | ||
1249 | struct udf_fileident_bh fibh; | ||
1250 | |||
1251 | dotdot.d_name.name = ".."; | ||
1252 | dotdot.d_name.len = 2; | ||
1253 | |||
1254 | lock_kernel(); | ||
1255 | if (!udf_find_entry(child->d_inode, &dotdot, &fibh, &cfi)) | ||
1256 | goto out_unlock; | ||
1257 | |||
1258 | if (fibh.sbh != fibh.ebh) | ||
1259 | brelse(fibh.ebh); | ||
1260 | brelse(fibh.sbh); | ||
1261 | |||
1262 | inode = udf_iget(child->d_inode->i_sb, | ||
1263 | lelb_to_cpu(cfi.icb.extLocation)); | ||
1264 | if (!inode) | ||
1265 | goto out_unlock; | ||
1266 | unlock_kernel(); | ||
1267 | |||
1268 | parent = d_alloc_anon(inode); | ||
1269 | if (!parent) { | ||
1270 | iput(inode); | ||
1271 | parent = ERR_PTR(-ENOMEM); | ||
1272 | } | ||
1273 | |||
1274 | return parent; | ||
1275 | out_unlock: | ||
1276 | unlock_kernel(); | ||
1277 | return ERR_PTR(-EACCES); | ||
1278 | } | ||
1279 | |||
1280 | |||
1281 | static struct dentry *udf_nfs_get_inode(struct super_block *sb, u32 block, | ||
1282 | u16 partref, __u32 generation) | ||
1283 | { | ||
1284 | struct inode *inode; | ||
1285 | struct dentry *result; | ||
1286 | kernel_lb_addr loc; | ||
1287 | |||
1288 | if (block == 0) | ||
1289 | return ERR_PTR(-ESTALE); | ||
1290 | |||
1291 | loc.logicalBlockNum = block; | ||
1292 | loc.partitionReferenceNum = partref; | ||
1293 | inode = udf_iget(sb, loc); | ||
1294 | |||
1295 | if (inode == NULL) | ||
1296 | return ERR_PTR(-ENOMEM); | ||
1297 | |||
1298 | if (generation && inode->i_generation != generation) { | ||
1299 | iput(inode); | ||
1300 | return ERR_PTR(-ESTALE); | ||
1301 | } | ||
1302 | result = d_alloc_anon(inode); | ||
1303 | if (!result) { | ||
1304 | iput(inode); | ||
1305 | return ERR_PTR(-ENOMEM); | ||
1306 | } | ||
1307 | return result; | ||
1308 | } | ||
1309 | |||
1310 | static struct dentry *udf_fh_to_dentry(struct super_block *sb, | ||
1311 | struct fid *fid, int fh_len, int fh_type) | ||
1312 | { | ||
1313 | if ((fh_len != 3 && fh_len != 5) || | ||
1314 | (fh_type != FILEID_UDF_WITH_PARENT && | ||
1315 | fh_type != FILEID_UDF_WITHOUT_PARENT)) | ||
1316 | return NULL; | ||
1317 | |||
1318 | return udf_nfs_get_inode(sb, fid->udf.block, fid->udf.partref, | ||
1319 | fid->udf.generation); | ||
1320 | } | ||
1321 | |||
1322 | static struct dentry *udf_fh_to_parent(struct super_block *sb, | ||
1323 | struct fid *fid, int fh_len, int fh_type) | ||
1324 | { | ||
1325 | if (fh_len != 5 || fh_type != FILEID_UDF_WITH_PARENT) | ||
1326 | return NULL; | ||
1327 | |||
1328 | return udf_nfs_get_inode(sb, fid->udf.parent_block, | ||
1329 | fid->udf.parent_partref, | ||
1330 | fid->udf.parent_generation); | ||
1331 | } | ||
1332 | static int udf_encode_fh(struct dentry *de, __u32 *fh, int *lenp, | ||
1333 | int connectable) | ||
1334 | { | ||
1335 | int len = *lenp; | ||
1336 | struct inode *inode = de->d_inode; | ||
1337 | kernel_lb_addr location = UDF_I(inode)->i_location; | ||
1338 | struct fid *fid = (struct fid *)fh; | ||
1339 | int type = FILEID_UDF_WITHOUT_PARENT; | ||
1340 | |||
1341 | if (len < 3 || (connectable && len < 5)) | ||
1342 | return 255; | ||
1343 | |||
1344 | *lenp = 3; | ||
1345 | fid->udf.block = location.logicalBlockNum; | ||
1346 | fid->udf.partref = location.partitionReferenceNum; | ||
1347 | fid->udf.generation = inode->i_generation; | ||
1348 | |||
1349 | if (connectable && !S_ISDIR(inode->i_mode)) { | ||
1350 | spin_lock(&de->d_lock); | ||
1351 | inode = de->d_parent->d_inode; | ||
1352 | location = UDF_I(inode)->i_location; | ||
1353 | fid->udf.parent_block = location.logicalBlockNum; | ||
1354 | fid->udf.parent_partref = location.partitionReferenceNum; | ||
1355 | fid->udf.parent_generation = inode->i_generation; | ||
1356 | spin_unlock(&de->d_lock); | ||
1357 | *lenp = 5; | ||
1358 | type = FILEID_UDF_WITH_PARENT; | ||
1359 | } | ||
1360 | |||
1361 | return type; | ||
1362 | } | ||
1363 | |||
1364 | const struct export_operations udf_export_ops = { | ||
1365 | .encode_fh = udf_encode_fh, | ||
1366 | .fh_to_dentry = udf_fh_to_dentry, | ||
1367 | .fh_to_parent = udf_fh_to_parent, | ||
1368 | .get_parent = udf_get_parent, | ||
1369 | }; | ||
1370 | |||
1235 | const struct inode_operations udf_dir_inode_operations = { | 1371 | const struct inode_operations udf_dir_inode_operations = { |
1236 | .lookup = udf_lookup, | 1372 | .lookup = udf_lookup, |
1237 | .create = udf_create, | 1373 | .create = udf_create, |
diff --git a/fs/udf/super.c b/fs/udf/super.c index b564fc140fe4..260f4b82c799 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c | |||
@@ -1933,6 +1933,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) | |||
1933 | 1933 | ||
1934 | /* Fill in the rest of the superblock */ | 1934 | /* Fill in the rest of the superblock */ |
1935 | sb->s_op = &udf_sb_ops; | 1935 | sb->s_op = &udf_sb_ops; |
1936 | sb->s_export_op = &udf_export_ops; | ||
1936 | sb->dq_op = NULL; | 1937 | sb->dq_op = NULL; |
1937 | sb->s_dirt = 0; | 1938 | sb->s_dirt = 0; |
1938 | sb->s_magic = UDF_SUPER_MAGIC; | 1939 | sb->s_magic = UDF_SUPER_MAGIC; |
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index f3f45d029277..8fa9c2d70911 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h | |||
@@ -73,6 +73,7 @@ struct task_struct; | |||
73 | struct buffer_head; | 73 | struct buffer_head; |
74 | struct super_block; | 74 | struct super_block; |
75 | 75 | ||
76 | extern const struct export_operations udf_export_ops; | ||
76 | extern const struct inode_operations udf_dir_inode_operations; | 77 | extern const struct inode_operations udf_dir_inode_operations; |
77 | extern const struct file_operations udf_dir_operations; | 78 | extern const struct file_operations udf_dir_operations; |
78 | extern const struct inode_operations udf_file_inode_operations; | 79 | extern const struct inode_operations udf_file_inode_operations; |
diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index de8387b7ceb6..f5abd1306638 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h | |||
@@ -33,6 +33,19 @@ enum fid_type { | |||
33 | * 32 bit parent directory inode number. | 33 | * 32 bit parent directory inode number. |
34 | */ | 34 | */ |
35 | FILEID_INO32_GEN_PARENT = 2, | 35 | FILEID_INO32_GEN_PARENT = 2, |
36 | |||
37 | /* | ||
38 | * 32 bit block number, 16 bit partition reference, | ||
39 | * 16 bit unused, 32 bit generation number. | ||
40 | */ | ||
41 | FILEID_UDF_WITHOUT_PARENT = 0x51, | ||
42 | |||
43 | /* | ||
44 | * 32 bit block number, 16 bit partition reference, | ||
45 | * 16 bit unused, 32 bit generation number, | ||
46 | * 32 bit parent block number, 32 bit parent generation number | ||
47 | */ | ||
48 | FILEID_UDF_WITH_PARENT = 0x52, | ||
36 | }; | 49 | }; |
37 | 50 | ||
38 | struct fid { | 51 | struct fid { |
@@ -43,6 +56,14 @@ struct fid { | |||
43 | u32 parent_ino; | 56 | u32 parent_ino; |
44 | u32 parent_gen; | 57 | u32 parent_gen; |
45 | } i32; | 58 | } i32; |
59 | struct { | ||
60 | u32 block; | ||
61 | u16 partref; | ||
62 | u16 parent_partref; | ||
63 | u32 generation; | ||
64 | u32 parent_block; | ||
65 | u32 parent_generation; | ||
66 | } udf; | ||
46 | __u32 raw[0]; | 67 | __u32 raw[0]; |
47 | }; | 68 | }; |
48 | }; | 69 | }; |