diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-10-18 17:20:16 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-10-18 17:20:16 -0400 |
commit | 834f2a4a1554dc5b2598038b3fe8703defcbe467 (patch) | |
tree | d27a000a2dd03029e6e2472c6e13801688185b51 /fs | |
parent | 039c4d7a82d8268ec71f59679460b41d0dd9b225 (diff) |
VFS: Allow the filesystem to return a full file pointer on open intent
This is needed by NFSv4 for atomicity reasons: our open command is in
fact a lookup+open, so we need to be able to propagate open context
information from lookup() into the resulting struct file's
private_data field.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/exec.c | 12 | ||||
-rw-r--r-- | fs/namei.c | 93 | ||||
-rw-r--r-- | fs/open.c | 79 |
3 files changed, 157 insertions, 27 deletions
@@ -126,8 +126,7 @@ asmlinkage long sys_uselib(const char __user * library) | |||
126 | struct nameidata nd; | 126 | struct nameidata nd; |
127 | int error; | 127 | int error; |
128 | 128 | ||
129 | nd.intent.open.flags = FMODE_READ; | 129 | error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ); |
130 | error = __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd); | ||
131 | if (error) | 130 | if (error) |
132 | goto out; | 131 | goto out; |
133 | 132 | ||
@@ -139,7 +138,7 @@ asmlinkage long sys_uselib(const char __user * library) | |||
139 | if (error) | 138 | if (error) |
140 | goto exit; | 139 | goto exit; |
141 | 140 | ||
142 | file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); | 141 | file = nameidata_to_filp(&nd, O_RDONLY); |
143 | error = PTR_ERR(file); | 142 | error = PTR_ERR(file); |
144 | if (IS_ERR(file)) | 143 | if (IS_ERR(file)) |
145 | goto out; | 144 | goto out; |
@@ -167,6 +166,7 @@ asmlinkage long sys_uselib(const char __user * library) | |||
167 | out: | 166 | out: |
168 | return error; | 167 | return error; |
169 | exit: | 168 | exit: |
169 | release_open_intent(&nd); | ||
170 | path_release(&nd); | 170 | path_release(&nd); |
171 | goto out; | 171 | goto out; |
172 | } | 172 | } |
@@ -490,8 +490,7 @@ struct file *open_exec(const char *name) | |||
490 | int err; | 490 | int err; |
491 | struct file *file; | 491 | struct file *file; |
492 | 492 | ||
493 | nd.intent.open.flags = FMODE_READ; | 493 | err = path_lookup_open(name, LOOKUP_FOLLOW, &nd, FMODE_READ); |
494 | err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd); | ||
495 | file = ERR_PTR(err); | 494 | file = ERR_PTR(err); |
496 | 495 | ||
497 | if (!err) { | 496 | if (!err) { |
@@ -504,7 +503,7 @@ struct file *open_exec(const char *name) | |||
504 | err = -EACCES; | 503 | err = -EACCES; |
505 | file = ERR_PTR(err); | 504 | file = ERR_PTR(err); |
506 | if (!err) { | 505 | if (!err) { |
507 | file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); | 506 | file = nameidata_to_filp(&nd, O_RDONLY); |
508 | if (!IS_ERR(file)) { | 507 | if (!IS_ERR(file)) { |
509 | err = deny_write_access(file); | 508 | err = deny_write_access(file); |
510 | if (err) { | 509 | if (err) { |
@@ -516,6 +515,7 @@ out: | |||
516 | return file; | 515 | return file; |
517 | } | 516 | } |
518 | } | 517 | } |
518 | release_open_intent(&nd); | ||
519 | path_release(&nd); | 519 | path_release(&nd); |
520 | } | 520 | } |
521 | goto out; | 521 | goto out; |
diff --git a/fs/namei.c b/fs/namei.c index aa62dbda93a..0d1dff7d3d9 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/syscalls.h> | 28 | #include <linux/syscalls.h> |
29 | #include <linux/mount.h> | 29 | #include <linux/mount.h> |
30 | #include <linux/audit.h> | 30 | #include <linux/audit.h> |
31 | #include <linux/file.h> | ||
31 | #include <asm/namei.h> | 32 | #include <asm/namei.h> |
32 | #include <asm/uaccess.h> | 33 | #include <asm/uaccess.h> |
33 | 34 | ||
@@ -317,6 +318,18 @@ void path_release_on_umount(struct nameidata *nd) | |||
317 | mntput_no_expire(nd->mnt); | 318 | mntput_no_expire(nd->mnt); |
318 | } | 319 | } |
319 | 320 | ||
321 | /** | ||
322 | * release_open_intent - free up open intent resources | ||
323 | * @nd: pointer to nameidata | ||
324 | */ | ||
325 | void release_open_intent(struct nameidata *nd) | ||
326 | { | ||
327 | if (nd->intent.open.file->f_dentry == NULL) | ||
328 | put_filp(nd->intent.open.file); | ||
329 | else | ||
330 | fput(nd->intent.open.file); | ||
331 | } | ||
332 | |||
320 | /* | 333 | /* |
321 | * Internal lookup() using the new generic dcache. | 334 | * Internal lookup() using the new generic dcache. |
322 | * SMP-safe | 335 | * SMP-safe |
@@ -1052,6 +1065,70 @@ out: | |||
1052 | return retval; | 1065 | return retval; |
1053 | } | 1066 | } |
1054 | 1067 | ||
1068 | static int __path_lookup_intent_open(const char *name, unsigned int lookup_flags, | ||
1069 | struct nameidata *nd, int open_flags, int create_mode) | ||
1070 | { | ||
1071 | struct file *filp = get_empty_filp(); | ||
1072 | int err; | ||
1073 | |||
1074 | if (filp == NULL) | ||
1075 | return -ENFILE; | ||
1076 | nd->intent.open.file = filp; | ||
1077 | nd->intent.open.flags = open_flags; | ||
1078 | nd->intent.open.create_mode = create_mode; | ||
1079 | err = path_lookup(name, lookup_flags|LOOKUP_OPEN, nd); | ||
1080 | if (IS_ERR(nd->intent.open.file)) { | ||
1081 | if (err == 0) { | ||
1082 | err = PTR_ERR(nd->intent.open.file); | ||
1083 | path_release(nd); | ||
1084 | } | ||
1085 | } else if (err != 0) | ||
1086 | release_open_intent(nd); | ||
1087 | return err; | ||
1088 | } | ||
1089 | |||
1090 | /** | ||
1091 | * path_lookup_open - lookup a file path with open intent | ||
1092 | * @name: pointer to file name | ||
1093 | * @lookup_flags: lookup intent flags | ||
1094 | * @nd: pointer to nameidata | ||
1095 | * @open_flags: open intent flags | ||
1096 | */ | ||
1097 | int path_lookup_open(const char *name, unsigned int lookup_flags, | ||
1098 | struct nameidata *nd, int open_flags) | ||
1099 | { | ||
1100 | return __path_lookup_intent_open(name, lookup_flags, nd, | ||
1101 | open_flags, 0); | ||
1102 | } | ||
1103 | |||
1104 | /** | ||
1105 | * path_lookup_create - lookup a file path with open + create intent | ||
1106 | * @name: pointer to file name | ||
1107 | * @lookup_flags: lookup intent flags | ||
1108 | * @nd: pointer to nameidata | ||
1109 | * @open_flags: open intent flags | ||
1110 | * @create_mode: create intent flags | ||
1111 | */ | ||
1112 | int path_lookup_create(const char *name, unsigned int lookup_flags, | ||
1113 | struct nameidata *nd, int open_flags, int create_mode) | ||
1114 | { | ||
1115 | return __path_lookup_intent_open(name, lookup_flags|LOOKUP_CREATE, nd, | ||
1116 | open_flags, create_mode); | ||
1117 | } | ||
1118 | |||
1119 | int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags, | ||
1120 | struct nameidata *nd, int open_flags) | ||
1121 | { | ||
1122 | char *tmp = getname(name); | ||
1123 | int err = PTR_ERR(tmp); | ||
1124 | |||
1125 | if (!IS_ERR(tmp)) { | ||
1126 | err = __path_lookup_intent_open(tmp, lookup_flags, nd, open_flags, 0); | ||
1127 | putname(tmp); | ||
1128 | } | ||
1129 | return err; | ||
1130 | } | ||
1131 | |||
1055 | /* | 1132 | /* |
1056 | * Restricted form of lookup. Doesn't follow links, single-component only, | 1133 | * Restricted form of lookup. Doesn't follow links, single-component only, |
1057 | * needs parent already locked. Doesn't follow mounts. | 1134 | * needs parent already locked. Doesn't follow mounts. |
@@ -1416,27 +1493,27 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) | |||
1416 | */ | 1493 | */ |
1417 | int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) | 1494 | int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) |
1418 | { | 1495 | { |
1419 | int acc_mode, error = 0; | 1496 | int acc_mode, error; |
1420 | struct path path; | 1497 | struct path path; |
1421 | struct dentry *dir; | 1498 | struct dentry *dir; |
1422 | int count = 0; | 1499 | int count = 0; |
1423 | 1500 | ||
1424 | acc_mode = ACC_MODE(flag); | 1501 | acc_mode = ACC_MODE(flag); |
1425 | 1502 | ||
1503 | /* O_TRUNC implies we need access checks for write permissions */ | ||
1504 | if (flag & O_TRUNC) | ||
1505 | acc_mode |= MAY_WRITE; | ||
1506 | |||
1426 | /* Allow the LSM permission hook to distinguish append | 1507 | /* Allow the LSM permission hook to distinguish append |
1427 | access from general write access. */ | 1508 | access from general write access. */ |
1428 | if (flag & O_APPEND) | 1509 | if (flag & O_APPEND) |
1429 | acc_mode |= MAY_APPEND; | 1510 | acc_mode |= MAY_APPEND; |
1430 | 1511 | ||
1431 | /* Fill in the open() intent data */ | ||
1432 | nd->intent.open.flags = flag; | ||
1433 | nd->intent.open.create_mode = mode; | ||
1434 | |||
1435 | /* | 1512 | /* |
1436 | * The simplest case - just a plain lookup. | 1513 | * The simplest case - just a plain lookup. |
1437 | */ | 1514 | */ |
1438 | if (!(flag & O_CREAT)) { | 1515 | if (!(flag & O_CREAT)) { |
1439 | error = path_lookup(pathname, lookup_flags(flag)|LOOKUP_OPEN, nd); | 1516 | error = path_lookup_open(pathname, lookup_flags(flag), nd, flag); |
1440 | if (error) | 1517 | if (error) |
1441 | return error; | 1518 | return error; |
1442 | goto ok; | 1519 | goto ok; |
@@ -1445,7 +1522,7 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) | |||
1445 | /* | 1522 | /* |
1446 | * Create - we need to know the parent. | 1523 | * Create - we need to know the parent. |
1447 | */ | 1524 | */ |
1448 | error = path_lookup(pathname, LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE, nd); | 1525 | error = path_lookup_create(pathname, LOOKUP_PARENT, nd, flag, mode); |
1449 | if (error) | 1526 | if (error) |
1450 | return error; | 1527 | return error; |
1451 | 1528 | ||
@@ -1520,6 +1597,8 @@ ok: | |||
1520 | exit_dput: | 1597 | exit_dput: |
1521 | dput_path(&path, nd); | 1598 | dput_path(&path, nd); |
1522 | exit: | 1599 | exit: |
1600 | if (!IS_ERR(nd->intent.open.file)) | ||
1601 | release_open_intent(nd); | ||
1523 | path_release(nd); | 1602 | path_release(nd); |
1524 | return error; | 1603 | return error; |
1525 | 1604 | ||
@@ -739,7 +739,8 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) | |||
739 | } | 739 | } |
740 | 740 | ||
741 | static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | 741 | static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, |
742 | int flags, struct file *f) | 742 | int flags, struct file *f, |
743 | int (*open)(struct inode *, struct file *)) | ||
743 | { | 744 | { |
744 | struct inode *inode; | 745 | struct inode *inode; |
745 | int error; | 746 | int error; |
@@ -761,11 +762,14 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, | |||
761 | f->f_op = fops_get(inode->i_fop); | 762 | f->f_op = fops_get(inode->i_fop); |
762 | file_move(f, &inode->i_sb->s_files); | 763 | file_move(f, &inode->i_sb->s_files); |
763 | 764 | ||
764 | if (f->f_op && f->f_op->open) { | 765 | if (!open && f->f_op) |
765 | error = f->f_op->open(inode,f); | 766 | open = f->f_op->open; |
767 | if (open) { | ||
768 | error = open(inode, f); | ||
766 | if (error) | 769 | if (error) |
767 | goto cleanup_all; | 770 | goto cleanup_all; |
768 | } | 771 | } |
772 | |||
769 | f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); | 773 | f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); |
770 | 774 | ||
771 | file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); | 775 | file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping); |
@@ -814,28 +818,75 @@ struct file *filp_open(const char * filename, int flags, int mode) | |||
814 | { | 818 | { |
815 | int namei_flags, error; | 819 | int namei_flags, error; |
816 | struct nameidata nd; | 820 | struct nameidata nd; |
817 | struct file *f; | ||
818 | 821 | ||
819 | namei_flags = flags; | 822 | namei_flags = flags; |
820 | if ((namei_flags+1) & O_ACCMODE) | 823 | if ((namei_flags+1) & O_ACCMODE) |
821 | namei_flags++; | 824 | namei_flags++; |
822 | if (namei_flags & O_TRUNC) | ||
823 | namei_flags |= 2; | ||
824 | |||
825 | error = -ENFILE; | ||
826 | f = get_empty_filp(); | ||
827 | if (f == NULL) | ||
828 | return ERR_PTR(error); | ||
829 | 825 | ||
830 | error = open_namei(filename, namei_flags, mode, &nd); | 826 | error = open_namei(filename, namei_flags, mode, &nd); |
831 | if (!error) | 827 | if (!error) |
832 | return __dentry_open(nd.dentry, nd.mnt, flags, f); | 828 | return nameidata_to_filp(&nd, flags); |
833 | 829 | ||
834 | put_filp(f); | ||
835 | return ERR_PTR(error); | 830 | return ERR_PTR(error); |
836 | } | 831 | } |
837 | EXPORT_SYMBOL(filp_open); | 832 | EXPORT_SYMBOL(filp_open); |
838 | 833 | ||
834 | /** | ||
835 | * lookup_instantiate_filp - instantiates the open intent filp | ||
836 | * @nd: pointer to nameidata | ||
837 | * @dentry: pointer to dentry | ||
838 | * @open: open callback | ||
839 | * | ||
840 | * Helper for filesystems that want to use lookup open intents and pass back | ||
841 | * a fully instantiated struct file to the caller. | ||
842 | * This function is meant to be called from within a filesystem's | ||
843 | * lookup method. | ||
844 | * Note that in case of error, nd->intent.open.file is destroyed, but the | ||
845 | * path information remains valid. | ||
846 | * If the open callback is set to NULL, then the standard f_op->open() | ||
847 | * filesystem callback is substituted. | ||
848 | */ | ||
849 | struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, | ||
850 | int (*open)(struct inode *, struct file *)) | ||
851 | { | ||
852 | if (IS_ERR(nd->intent.open.file)) | ||
853 | goto out; | ||
854 | if (IS_ERR(dentry)) | ||
855 | goto out_err; | ||
856 | nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->mnt), | ||
857 | nd->intent.open.flags - 1, | ||
858 | nd->intent.open.file, | ||
859 | open); | ||
860 | out: | ||
861 | return nd->intent.open.file; | ||
862 | out_err: | ||
863 | release_open_intent(nd); | ||
864 | nd->intent.open.file = (struct file *)dentry; | ||
865 | goto out; | ||
866 | } | ||
867 | EXPORT_SYMBOL_GPL(lookup_instantiate_filp); | ||
868 | |||
869 | /** | ||
870 | * nameidata_to_filp - convert a nameidata to an open filp. | ||
871 | * @nd: pointer to nameidata | ||
872 | * @flags: open flags | ||
873 | * | ||
874 | * Note that this function destroys the original nameidata | ||
875 | */ | ||
876 | struct file *nameidata_to_filp(struct nameidata *nd, int flags) | ||
877 | { | ||
878 | struct file *filp; | ||
879 | |||
880 | /* Pick up the filp from the open intent */ | ||
881 | filp = nd->intent.open.file; | ||
882 | /* Has the filesystem initialised the file for us? */ | ||
883 | if (filp->f_dentry == NULL) | ||
884 | filp = __dentry_open(nd->dentry, nd->mnt, flags, filp, NULL); | ||
885 | else | ||
886 | path_release(nd); | ||
887 | return filp; | ||
888 | } | ||
889 | |||
839 | struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) | 890 | struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) |
840 | { | 891 | { |
841 | int error; | 892 | int error; |
@@ -846,7 +897,7 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) | |||
846 | if (f == NULL) | 897 | if (f == NULL) |
847 | return ERR_PTR(error); | 898 | return ERR_PTR(error); |
848 | 899 | ||
849 | return __dentry_open(dentry, mnt, flags, f); | 900 | return __dentry_open(dentry, mnt, flags, f, NULL); |
850 | } | 901 | } |
851 | EXPORT_SYMBOL(dentry_open); | 902 | EXPORT_SYMBOL(dentry_open); |
852 | 903 | ||