diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/coda |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/coda')
-rw-r--r-- | fs/coda/Makefile | 12 | ||||
-rw-r--r-- | fs/coda/cache.c | 120 | ||||
-rw-r--r-- | fs/coda/cnode.c | 172 | ||||
-rw-r--r-- | fs/coda/coda_linux.c | 197 | ||||
-rw-r--r-- | fs/coda/dir.c | 704 | ||||
-rw-r--r-- | fs/coda/file.c | 300 | ||||
-rw-r--r-- | fs/coda/inode.c | 321 | ||||
-rw-r--r-- | fs/coda/pioctl.c | 95 | ||||
-rw-r--r-- | fs/coda/psdev.c | 462 | ||||
-rw-r--r-- | fs/coda/symlink.c | 55 | ||||
-rw-r--r-- | fs/coda/sysctl.c | 254 | ||||
-rw-r--r-- | fs/coda/upcall.c | 914 |
12 files changed, 3606 insertions, 0 deletions
diff --git a/fs/coda/Makefile b/fs/coda/Makefile new file mode 100644 index 000000000000..6c22e61da397 --- /dev/null +++ b/fs/coda/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile for the Linux Coda filesystem routines. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_CODA_FS) += coda.o | ||
6 | |||
7 | coda-objs := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o \ | ||
8 | coda_linux.o symlink.o pioctl.o sysctl.o | ||
9 | |||
10 | # If you want debugging output, please uncomment the following line. | ||
11 | |||
12 | # EXTRA_CFLAGS += -DDEBUG -DDEBUG_SMB_MALLOC=1 | ||
diff --git a/fs/coda/cache.c b/fs/coda/cache.c new file mode 100644 index 000000000000..80072fd9b7fa --- /dev/null +++ b/fs/coda/cache.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * Cache operations for Coda. | ||
3 | * For Linux 2.1: (C) 1997 Carnegie Mellon University | ||
4 | * For Linux 2.3: (C) 2000 Carnegie Mellon University | ||
5 | * | ||
6 | * Carnegie Mellon encourages users of this code to contribute improvements | ||
7 | * to the Coda project http://www.coda.cs.cmu.edu/ <coda@cs.cmu.edu>. | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/time.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/stat.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <asm/uaccess.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/list.h> | ||
19 | |||
20 | #include <linux/coda.h> | ||
21 | #include <linux/coda_linux.h> | ||
22 | #include <linux/coda_psdev.h> | ||
23 | #include <linux/coda_fs_i.h> | ||
24 | #include <linux/coda_cache.h> | ||
25 | |||
26 | static atomic_t permission_epoch = ATOMIC_INIT(0); | ||
27 | |||
28 | /* replace or extend an acl cache hit */ | ||
29 | void coda_cache_enter(struct inode *inode, int mask) | ||
30 | { | ||
31 | struct coda_inode_info *cii = ITOC(inode); | ||
32 | |||
33 | cii->c_cached_epoch = atomic_read(&permission_epoch); | ||
34 | if (cii->c_uid != current->fsuid) { | ||
35 | cii->c_uid = current->fsuid; | ||
36 | cii->c_cached_perm = mask; | ||
37 | } else | ||
38 | cii->c_cached_perm |= mask; | ||
39 | } | ||
40 | |||
41 | /* remove cached acl from an inode */ | ||
42 | void coda_cache_clear_inode(struct inode *inode) | ||
43 | { | ||
44 | struct coda_inode_info *cii = ITOC(inode); | ||
45 | cii->c_cached_perm = 0; | ||
46 | } | ||
47 | |||
48 | /* remove all acl caches */ | ||
49 | void coda_cache_clear_all(struct super_block *sb) | ||
50 | { | ||
51 | struct coda_sb_info *sbi; | ||
52 | |||
53 | sbi = coda_sbp(sb); | ||
54 | if (!sbi) BUG(); | ||
55 | |||
56 | atomic_inc(&permission_epoch); | ||
57 | } | ||
58 | |||
59 | |||
60 | /* check if the mask has been matched against the acl already */ | ||
61 | int coda_cache_check(struct inode *inode, int mask) | ||
62 | { | ||
63 | struct coda_inode_info *cii = ITOC(inode); | ||
64 | int hit; | ||
65 | |||
66 | hit = (mask & cii->c_cached_perm) == mask && | ||
67 | cii->c_uid == current->fsuid && | ||
68 | cii->c_cached_epoch == atomic_read(&permission_epoch); | ||
69 | |||
70 | return hit; | ||
71 | } | ||
72 | |||
73 | |||
74 | /* Purging dentries and children */ | ||
75 | /* The following routines drop dentries which are not | ||
76 | in use and flag dentries which are in use to be | ||
77 | zapped later. | ||
78 | |||
79 | The flags are detected by: | ||
80 | - coda_dentry_revalidate (for lookups) if the flag is C_PURGE | ||
81 | - coda_dentry_delete: to remove dentry from the cache when d_count | ||
82 | falls to zero | ||
83 | - an inode method coda_revalidate (for attributes) if the | ||
84 | flag is C_VATTR | ||
85 | */ | ||
86 | |||
87 | /* this won't do any harm: just flag all children */ | ||
88 | static void coda_flag_children(struct dentry *parent, int flag) | ||
89 | { | ||
90 | struct list_head *child; | ||
91 | struct dentry *de; | ||
92 | |||
93 | spin_lock(&dcache_lock); | ||
94 | list_for_each(child, &parent->d_subdirs) | ||
95 | { | ||
96 | de = list_entry(child, struct dentry, d_child); | ||
97 | /* don't know what to do with negative dentries */ | ||
98 | if ( ! de->d_inode ) | ||
99 | continue; | ||
100 | coda_flag_inode(de->d_inode, flag); | ||
101 | } | ||
102 | spin_unlock(&dcache_lock); | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | void coda_flag_inode_children(struct inode *inode, int flag) | ||
107 | { | ||
108 | struct dentry *alias_de; | ||
109 | |||
110 | if ( !inode || !S_ISDIR(inode->i_mode)) | ||
111 | return; | ||
112 | |||
113 | alias_de = d_find_alias(inode); | ||
114 | if (!alias_de) | ||
115 | return; | ||
116 | coda_flag_children(alias_de, flag); | ||
117 | shrink_dcache_parent(alias_de); | ||
118 | dput(alias_de); | ||
119 | } | ||
120 | |||
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c new file mode 100644 index 000000000000..23aeef5aa814 --- /dev/null +++ b/fs/coda/cnode.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* cnode related routines for the coda kernel code | ||
2 | (C) 1996 Peter Braam | ||
3 | */ | ||
4 | |||
5 | #include <linux/types.h> | ||
6 | #include <linux/string.h> | ||
7 | #include <linux/time.h> | ||
8 | |||
9 | #include <linux/coda.h> | ||
10 | #include <linux/coda_linux.h> | ||
11 | #include <linux/coda_fs_i.h> | ||
12 | #include <linux/coda_psdev.h> | ||
13 | |||
14 | static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2) | ||
15 | { | ||
16 | return memcmp(fid1, fid2, sizeof(*fid1)) == 0; | ||
17 | } | ||
18 | |||
19 | static struct inode_operations coda_symlink_inode_operations = { | ||
20 | .readlink = generic_readlink, | ||
21 | .follow_link = page_follow_link_light, | ||
22 | .put_link = page_put_link, | ||
23 | .setattr = coda_setattr, | ||
24 | }; | ||
25 | |||
26 | /* cnode.c */ | ||
27 | static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr) | ||
28 | { | ||
29 | coda_vattr_to_iattr(inode, attr); | ||
30 | |||
31 | if (S_ISREG(inode->i_mode)) { | ||
32 | inode->i_op = &coda_file_inode_operations; | ||
33 | inode->i_fop = &coda_file_operations; | ||
34 | } else if (S_ISDIR(inode->i_mode)) { | ||
35 | inode->i_op = &coda_dir_inode_operations; | ||
36 | inode->i_fop = &coda_dir_operations; | ||
37 | } else if (S_ISLNK(inode->i_mode)) { | ||
38 | inode->i_op = &coda_symlink_inode_operations; | ||
39 | inode->i_data.a_ops = &coda_symlink_aops; | ||
40 | inode->i_mapping = &inode->i_data; | ||
41 | } else | ||
42 | init_special_inode(inode, inode->i_mode, huge_decode_dev(attr->va_rdev)); | ||
43 | } | ||
44 | |||
45 | static int coda_test_inode(struct inode *inode, void *data) | ||
46 | { | ||
47 | struct CodaFid *fid = (struct CodaFid *)data; | ||
48 | return coda_fideq(&(ITOC(inode)->c_fid), fid); | ||
49 | } | ||
50 | |||
51 | static int coda_set_inode(struct inode *inode, void *data) | ||
52 | { | ||
53 | struct CodaFid *fid = (struct CodaFid *)data; | ||
54 | ITOC(inode)->c_fid = *fid; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int coda_fail_inode(struct inode *inode, void *data) | ||
59 | { | ||
60 | return -1; | ||
61 | } | ||
62 | |||
63 | struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid, | ||
64 | struct coda_vattr * attr) | ||
65 | { | ||
66 | struct inode *inode; | ||
67 | struct coda_inode_info *cii; | ||
68 | unsigned long hash = coda_f2i(fid); | ||
69 | |||
70 | inode = iget5_locked(sb, hash, coda_test_inode, coda_set_inode, fid); | ||
71 | |||
72 | if (!inode) | ||
73 | return ERR_PTR(-ENOMEM); | ||
74 | |||
75 | if (inode->i_state & I_NEW) { | ||
76 | cii = ITOC(inode); | ||
77 | /* we still need to set i_ino for things like stat(2) */ | ||
78 | inode->i_ino = hash; | ||
79 | cii->c_mapcount = 0; | ||
80 | unlock_new_inode(inode); | ||
81 | } | ||
82 | |||
83 | /* always replace the attributes, type might have changed */ | ||
84 | coda_fill_inode(inode, attr); | ||
85 | return inode; | ||
86 | } | ||
87 | |||
88 | /* this is effectively coda_iget: | ||
89 | - get attributes (might be cached) | ||
90 | - get the inode for the fid using vfs iget | ||
91 | - link the two up if this is needed | ||
92 | - fill in the attributes | ||
93 | */ | ||
94 | int coda_cnode_make(struct inode **inode, struct CodaFid *fid, struct super_block *sb) | ||
95 | { | ||
96 | struct coda_vattr attr; | ||
97 | int error; | ||
98 | |||
99 | /* We get inode numbers from Venus -- see venus source */ | ||
100 | error = venus_getattr(sb, fid, &attr); | ||
101 | if ( error ) { | ||
102 | *inode = NULL; | ||
103 | return error; | ||
104 | } | ||
105 | |||
106 | *inode = coda_iget(sb, fid, &attr); | ||
107 | if ( IS_ERR(*inode) ) { | ||
108 | printk("coda_cnode_make: coda_iget failed\n"); | ||
109 | return PTR_ERR(*inode); | ||
110 | } | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | |||
115 | void coda_replace_fid(struct inode *inode, struct CodaFid *oldfid, | ||
116 | struct CodaFid *newfid) | ||
117 | { | ||
118 | struct coda_inode_info *cii; | ||
119 | unsigned long hash = coda_f2i(newfid); | ||
120 | |||
121 | cii = ITOC(inode); | ||
122 | |||
123 | if (!coda_fideq(&cii->c_fid, oldfid)) | ||
124 | BUG(); | ||
125 | |||
126 | /* replace fid and rehash inode */ | ||
127 | /* XXX we probably need to hold some lock here! */ | ||
128 | remove_inode_hash(inode); | ||
129 | cii->c_fid = *newfid; | ||
130 | inode->i_ino = hash; | ||
131 | __insert_inode_hash(inode, hash); | ||
132 | } | ||
133 | |||
134 | /* convert a fid to an inode. */ | ||
135 | struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb) | ||
136 | { | ||
137 | struct inode *inode; | ||
138 | unsigned long hash = coda_f2i(fid); | ||
139 | |||
140 | if ( !sb ) { | ||
141 | printk("coda_fid_to_inode: no sb!\n"); | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | inode = iget5_locked(sb, hash, coda_test_inode, coda_fail_inode, fid); | ||
146 | if ( !inode ) | ||
147 | return NULL; | ||
148 | |||
149 | /* we should never see newly created inodes because we intentionally | ||
150 | * fail in the initialization callback */ | ||
151 | BUG_ON(inode->i_state & I_NEW); | ||
152 | |||
153 | return inode; | ||
154 | } | ||
155 | |||
156 | /* the CONTROL inode is made without asking attributes from Venus */ | ||
157 | int coda_cnode_makectl(struct inode **inode, struct super_block *sb) | ||
158 | { | ||
159 | int error = -ENOMEM; | ||
160 | |||
161 | *inode = new_inode(sb); | ||
162 | if (*inode) { | ||
163 | (*inode)->i_ino = CTL_INO; | ||
164 | (*inode)->i_op = &coda_ioctl_inode_operations; | ||
165 | (*inode)->i_fop = &coda_ioctl_operations; | ||
166 | (*inode)->i_mode = 0444; | ||
167 | error = 0; | ||
168 | } | ||
169 | |||
170 | return error; | ||
171 | } | ||
172 | |||
diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c new file mode 100644 index 000000000000..5597080cb811 --- /dev/null +++ b/fs/coda/coda_linux.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* | ||
2 | * Inode operations for Coda filesystem | ||
3 | * Original version: (C) 1996 P. Braam and M. Callahan | ||
4 | * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University | ||
5 | * | ||
6 | * Carnegie Mellon encourages users to contribute improvements to | ||
7 | * the Coda project. Contact Peter Braam (coda@cs.cmu.edu). | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/time.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/stat.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <asm/uaccess.h> | ||
17 | #include <linux/string.h> | ||
18 | |||
19 | #include <linux/coda.h> | ||
20 | #include <linux/coda_linux.h> | ||
21 | #include <linux/coda_psdev.h> | ||
22 | #include <linux/coda_fs_i.h> | ||
23 | |||
24 | /* initialize the debugging variables */ | ||
25 | int coda_fake_statfs; | ||
26 | |||
27 | /* print a fid */ | ||
28 | char * coda_f2s(struct CodaFid *f) | ||
29 | { | ||
30 | static char s[60]; | ||
31 | #ifdef CONFIG_CODA_FS_OLD_API | ||
32 | sprintf(s, "(%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2]); | ||
33 | #else | ||
34 | sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]); | ||
35 | #endif | ||
36 | return s; | ||
37 | } | ||
38 | |||
39 | /* recognize special .CONTROL name */ | ||
40 | int coda_iscontrol(const char *name, size_t length) | ||
41 | { | ||
42 | return ((CODA_CONTROLLEN == length) && | ||
43 | (strncmp(name, CODA_CONTROL, CODA_CONTROLLEN) == 0)); | ||
44 | } | ||
45 | |||
46 | /* recognize /coda inode */ | ||
47 | int coda_isroot(struct inode *i) | ||
48 | { | ||
49 | return ( i->i_sb->s_root->d_inode == i ); | ||
50 | } | ||
51 | |||
52 | unsigned short coda_flags_to_cflags(unsigned short flags) | ||
53 | { | ||
54 | unsigned short coda_flags = 0; | ||
55 | |||
56 | if ((flags & O_ACCMODE) == O_RDONLY) | ||
57 | coda_flags |= C_O_READ; | ||
58 | |||
59 | if ((flags & O_ACCMODE) == O_RDWR) | ||
60 | coda_flags |= C_O_READ | C_O_WRITE; | ||
61 | |||
62 | if ((flags & O_ACCMODE) == O_WRONLY) | ||
63 | coda_flags |= C_O_WRITE; | ||
64 | |||
65 | if (flags & O_TRUNC) | ||
66 | coda_flags |= C_O_TRUNC; | ||
67 | |||
68 | if (flags & O_CREAT) | ||
69 | coda_flags |= C_O_CREAT; | ||
70 | |||
71 | if (flags & O_EXCL) | ||
72 | coda_flags |= C_O_EXCL; | ||
73 | |||
74 | return coda_flags; | ||
75 | } | ||
76 | |||
77 | |||
78 | /* utility functions below */ | ||
79 | void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) | ||
80 | { | ||
81 | int inode_type; | ||
82 | /* inode's i_flags, i_ino are set by iget | ||
83 | XXX: is this all we need ?? | ||
84 | */ | ||
85 | switch (attr->va_type) { | ||
86 | case C_VNON: | ||
87 | inode_type = 0; | ||
88 | break; | ||
89 | case C_VREG: | ||
90 | inode_type = S_IFREG; | ||
91 | break; | ||
92 | case C_VDIR: | ||
93 | inode_type = S_IFDIR; | ||
94 | break; | ||
95 | case C_VLNK: | ||
96 | inode_type = S_IFLNK; | ||
97 | break; | ||
98 | default: | ||
99 | inode_type = 0; | ||
100 | } | ||
101 | inode->i_mode |= inode_type; | ||
102 | |||
103 | if (attr->va_mode != (u_short) -1) | ||
104 | inode->i_mode = attr->va_mode | inode_type; | ||
105 | if (attr->va_uid != -1) | ||
106 | inode->i_uid = (uid_t) attr->va_uid; | ||
107 | if (attr->va_gid != -1) | ||
108 | inode->i_gid = (gid_t) attr->va_gid; | ||
109 | if (attr->va_nlink != -1) | ||
110 | inode->i_nlink = attr->va_nlink; | ||
111 | if (attr->va_size != -1) | ||
112 | inode->i_size = attr->va_size; | ||
113 | if (attr->va_blocksize != -1) | ||
114 | inode->i_blksize = attr->va_blocksize; | ||
115 | if (attr->va_size != -1) | ||
116 | inode->i_blocks = (attr->va_size + 511) >> 9; | ||
117 | if (attr->va_atime.tv_sec != -1) | ||
118 | inode->i_atime = attr->va_atime; | ||
119 | if (attr->va_mtime.tv_sec != -1) | ||
120 | inode->i_mtime = attr->va_mtime; | ||
121 | if (attr->va_ctime.tv_sec != -1) | ||
122 | inode->i_ctime = attr->va_ctime; | ||
123 | } | ||
124 | |||
125 | |||
126 | /* | ||
127 | * BSD sets attributes that need not be modified to -1. | ||
128 | * Linux uses the valid field to indicate what should be | ||
129 | * looked at. The BSD type field needs to be deduced from linux | ||
130 | * mode. | ||
131 | * So we have to do some translations here. | ||
132 | */ | ||
133 | |||
134 | void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) | ||
135 | { | ||
136 | unsigned int valid; | ||
137 | |||
138 | /* clean out */ | ||
139 | vattr->va_mode = (umode_t) -1; | ||
140 | vattr->va_uid = (vuid_t) -1; | ||
141 | vattr->va_gid = (vgid_t) -1; | ||
142 | vattr->va_size = (off_t) -1; | ||
143 | vattr->va_atime.tv_sec = (time_t) -1; | ||
144 | vattr->va_atime.tv_nsec = (time_t) -1; | ||
145 | vattr->va_mtime.tv_sec = (time_t) -1; | ||
146 | vattr->va_mtime.tv_nsec = (time_t) -1; | ||
147 | vattr->va_ctime.tv_sec = (time_t) -1; | ||
148 | vattr->va_ctime.tv_nsec = (time_t) -1; | ||
149 | vattr->va_type = C_VNON; | ||
150 | vattr->va_fileid = -1; | ||
151 | vattr->va_gen = -1; | ||
152 | vattr->va_bytes = -1; | ||
153 | vattr->va_nlink = -1; | ||
154 | vattr->va_blocksize = -1; | ||
155 | vattr->va_rdev = -1; | ||
156 | vattr->va_flags = 0; | ||
157 | |||
158 | /* determine the type */ | ||
159 | #if 0 | ||
160 | mode = iattr->ia_mode; | ||
161 | if ( S_ISDIR(mode) ) { | ||
162 | vattr->va_type = C_VDIR; | ||
163 | } else if ( S_ISREG(mode) ) { | ||
164 | vattr->va_type = C_VREG; | ||
165 | } else if ( S_ISLNK(mode) ) { | ||
166 | vattr->va_type = C_VLNK; | ||
167 | } else { | ||
168 | /* don't do others */ | ||
169 | vattr->va_type = C_VNON; | ||
170 | } | ||
171 | #endif | ||
172 | |||
173 | /* set those vattrs that need change */ | ||
174 | valid = iattr->ia_valid; | ||
175 | if ( valid & ATTR_MODE ) { | ||
176 | vattr->va_mode = iattr->ia_mode; | ||
177 | } | ||
178 | if ( valid & ATTR_UID ) { | ||
179 | vattr->va_uid = (vuid_t) iattr->ia_uid; | ||
180 | } | ||
181 | if ( valid & ATTR_GID ) { | ||
182 | vattr->va_gid = (vgid_t) iattr->ia_gid; | ||
183 | } | ||
184 | if ( valid & ATTR_SIZE ) { | ||
185 | vattr->va_size = iattr->ia_size; | ||
186 | } | ||
187 | if ( valid & ATTR_ATIME ) { | ||
188 | vattr->va_atime = iattr->ia_atime; | ||
189 | } | ||
190 | if ( valid & ATTR_MTIME ) { | ||
191 | vattr->va_mtime = iattr->ia_mtime; | ||
192 | } | ||
193 | if ( valid & ATTR_CTIME ) { | ||
194 | vattr->va_ctime = iattr->ia_ctime; | ||
195 | } | ||
196 | } | ||
197 | |||
diff --git a/fs/coda/dir.c b/fs/coda/dir.c new file mode 100644 index 000000000000..2391766e9c7c --- /dev/null +++ b/fs/coda/dir.c | |||
@@ -0,0 +1,704 @@ | |||
1 | |||
2 | /* | ||
3 | * Directory operations for Coda filesystem | ||
4 | * Original version: (C) 1996 P. Braam and M. Callahan | ||
5 | * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University | ||
6 | * | ||
7 | * Carnegie Mellon encourages users to contribute improvements to | ||
8 | * the Coda project. Contact Peter Braam (coda@cs.cmu.edu). | ||
9 | */ | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/time.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/file.h> | ||
16 | #include <linux/stat.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/smp_lock.h> | ||
20 | |||
21 | #include <asm/uaccess.h> | ||
22 | |||
23 | #include <linux/coda.h> | ||
24 | #include <linux/coda_linux.h> | ||
25 | #include <linux/coda_psdev.h> | ||
26 | #include <linux/coda_fs_i.h> | ||
27 | #include <linux/coda_cache.h> | ||
28 | #include <linux/coda_proc.h> | ||
29 | |||
30 | /* dir inode-ops */ | ||
31 | static int coda_create(struct inode *dir, struct dentry *new, int mode, struct nameidata *nd); | ||
32 | static struct dentry *coda_lookup(struct inode *dir, struct dentry *target, struct nameidata *nd); | ||
33 | static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, | ||
34 | struct dentry *entry); | ||
35 | static int coda_unlink(struct inode *dir_inode, struct dentry *entry); | ||
36 | static int coda_symlink(struct inode *dir_inode, struct dentry *entry, | ||
37 | const char *symname); | ||
38 | static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, int mode); | ||
39 | static int coda_rmdir(struct inode *dir_inode, struct dentry *entry); | ||
40 | static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, | ||
41 | struct inode *new_inode, struct dentry *new_dentry); | ||
42 | |||
43 | /* dir file-ops */ | ||
44 | static int coda_readdir(struct file *file, void *dirent, filldir_t filldir); | ||
45 | |||
46 | /* dentry ops */ | ||
47 | static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd); | ||
48 | static int coda_dentry_delete(struct dentry *); | ||
49 | |||
50 | /* support routines */ | ||
51 | static int coda_venus_readdir(struct file *filp, filldir_t filldir, | ||
52 | void *dirent, struct dentry *dir); | ||
53 | int coda_fsync(struct file *, struct dentry *dentry, int datasync); | ||
54 | |||
55 | /* same as fs/bad_inode.c */ | ||
56 | static int coda_return_EIO(void) | ||
57 | { | ||
58 | return -EIO; | ||
59 | } | ||
60 | #define CODA_EIO_ERROR ((void *) (coda_return_EIO)) | ||
61 | |||
62 | static struct dentry_operations coda_dentry_operations = | ||
63 | { | ||
64 | .d_revalidate = coda_dentry_revalidate, | ||
65 | .d_delete = coda_dentry_delete, | ||
66 | }; | ||
67 | |||
68 | struct inode_operations coda_dir_inode_operations = | ||
69 | { | ||
70 | .create = coda_create, | ||
71 | .lookup = coda_lookup, | ||
72 | .link = coda_link, | ||
73 | .unlink = coda_unlink, | ||
74 | .symlink = coda_symlink, | ||
75 | .mkdir = coda_mkdir, | ||
76 | .rmdir = coda_rmdir, | ||
77 | .mknod = CODA_EIO_ERROR, | ||
78 | .rename = coda_rename, | ||
79 | .permission = coda_permission, | ||
80 | .getattr = coda_getattr, | ||
81 | .setattr = coda_setattr, | ||
82 | }; | ||
83 | |||
84 | struct file_operations coda_dir_operations = { | ||
85 | .llseek = generic_file_llseek, | ||
86 | .read = generic_read_dir, | ||
87 | .readdir = coda_readdir, | ||
88 | .open = coda_open, | ||
89 | .flush = coda_flush, | ||
90 | .release = coda_release, | ||
91 | .fsync = coda_fsync, | ||
92 | }; | ||
93 | |||
94 | |||
95 | /* inode operations for directories */ | ||
96 | /* access routines: lookup, readlink, permission */ | ||
97 | static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, struct nameidata *nd) | ||
98 | { | ||
99 | struct inode *res_inode = NULL; | ||
100 | struct CodaFid resfid = { { 0, } }; | ||
101 | int dropme = 0; /* to indicate entry should not be cached */ | ||
102 | int type = 0; | ||
103 | int error = 0; | ||
104 | const char *name = entry->d_name.name; | ||
105 | size_t length = entry->d_name.len; | ||
106 | |||
107 | if ( length > CODA_MAXNAMLEN ) { | ||
108 | printk("name too long: lookup, %s (%*s)\n", | ||
109 | coda_i2s(dir), (int)length, name); | ||
110 | return ERR_PTR(-ENAMETOOLONG); | ||
111 | } | ||
112 | |||
113 | lock_kernel(); | ||
114 | /* control object, create inode on the fly */ | ||
115 | if (coda_isroot(dir) && coda_iscontrol(name, length)) { | ||
116 | error = coda_cnode_makectl(&res_inode, dir->i_sb); | ||
117 | dropme = 1; | ||
118 | goto exit; | ||
119 | } | ||
120 | |||
121 | error = venus_lookup(dir->i_sb, coda_i2f(dir), | ||
122 | (const char *)name, length, &type, &resfid); | ||
123 | |||
124 | res_inode = NULL; | ||
125 | if (!error) { | ||
126 | if (type & CODA_NOCACHE) { | ||
127 | type &= (~CODA_NOCACHE); | ||
128 | dropme = 1; | ||
129 | } | ||
130 | |||
131 | error = coda_cnode_make(&res_inode, &resfid, dir->i_sb); | ||
132 | if (error) { | ||
133 | unlock_kernel(); | ||
134 | return ERR_PTR(error); | ||
135 | } | ||
136 | } else if (error != -ENOENT) { | ||
137 | unlock_kernel(); | ||
138 | return ERR_PTR(error); | ||
139 | } | ||
140 | |||
141 | exit: | ||
142 | entry->d_time = 0; | ||
143 | entry->d_op = &coda_dentry_operations; | ||
144 | d_add(entry, res_inode); | ||
145 | if ( dropme ) { | ||
146 | d_drop(entry); | ||
147 | coda_flag_inode(res_inode, C_VATTR); | ||
148 | } | ||
149 | unlock_kernel(); | ||
150 | return NULL; | ||
151 | } | ||
152 | |||
153 | |||
154 | int coda_permission(struct inode *inode, int mask, struct nameidata *nd) | ||
155 | { | ||
156 | int error = 0; | ||
157 | |||
158 | if (!mask) | ||
159 | return 0; | ||
160 | |||
161 | lock_kernel(); | ||
162 | |||
163 | coda_vfs_stat.permission++; | ||
164 | |||
165 | if (coda_cache_check(inode, mask)) | ||
166 | goto out; | ||
167 | |||
168 | error = venus_access(inode->i_sb, coda_i2f(inode), mask); | ||
169 | |||
170 | if (!error) | ||
171 | coda_cache_enter(inode, mask); | ||
172 | |||
173 | out: | ||
174 | unlock_kernel(); | ||
175 | |||
176 | return error; | ||
177 | } | ||
178 | |||
179 | |||
180 | static inline void coda_dir_changed(struct inode *dir, int link) | ||
181 | { | ||
182 | #ifdef REQUERY_VENUS_FOR_MTIME | ||
183 | /* invalidate the directory cnode's attributes so we refetch the | ||
184 | * attributes from venus next time the inode is referenced */ | ||
185 | coda_flag_inode(dir, C_VATTR); | ||
186 | #else | ||
187 | /* optimistically we can also act as if our nose bleeds. The | ||
188 | * granularity of the mtime is coarse anyways so we might actually be | ||
189 | * right most of the time. Note: we only do this for directories. */ | ||
190 | dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; | ||
191 | #endif | ||
192 | if (link) | ||
193 | dir->i_nlink += link; | ||
194 | } | ||
195 | |||
196 | /* creation routines: create, mknod, mkdir, link, symlink */ | ||
197 | static int coda_create(struct inode *dir, struct dentry *de, int mode, struct nameidata *nd) | ||
198 | { | ||
199 | int error=0; | ||
200 | const char *name=de->d_name.name; | ||
201 | int length=de->d_name.len; | ||
202 | struct inode *inode; | ||
203 | struct CodaFid newfid; | ||
204 | struct coda_vattr attrs; | ||
205 | |||
206 | lock_kernel(); | ||
207 | coda_vfs_stat.create++; | ||
208 | |||
209 | if (coda_isroot(dir) && coda_iscontrol(name, length)) { | ||
210 | unlock_kernel(); | ||
211 | return -EPERM; | ||
212 | } | ||
213 | |||
214 | error = venus_create(dir->i_sb, coda_i2f(dir), name, length, | ||
215 | 0, mode, &newfid, &attrs); | ||
216 | |||
217 | if ( error ) { | ||
218 | unlock_kernel(); | ||
219 | d_drop(de); | ||
220 | return error; | ||
221 | } | ||
222 | |||
223 | inode = coda_iget(dir->i_sb, &newfid, &attrs); | ||
224 | if ( IS_ERR(inode) ) { | ||
225 | unlock_kernel(); | ||
226 | d_drop(de); | ||
227 | return PTR_ERR(inode); | ||
228 | } | ||
229 | |||
230 | /* invalidate the directory cnode's attributes */ | ||
231 | coda_dir_changed(dir, 0); | ||
232 | unlock_kernel(); | ||
233 | d_instantiate(de, inode); | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int coda_mkdir(struct inode *dir, struct dentry *de, int mode) | ||
238 | { | ||
239 | struct inode *inode; | ||
240 | struct coda_vattr attrs; | ||
241 | const char *name = de->d_name.name; | ||
242 | int len = de->d_name.len; | ||
243 | int error; | ||
244 | struct CodaFid newfid; | ||
245 | |||
246 | lock_kernel(); | ||
247 | coda_vfs_stat.mkdir++; | ||
248 | |||
249 | if (coda_isroot(dir) && coda_iscontrol(name, len)) { | ||
250 | unlock_kernel(); | ||
251 | return -EPERM; | ||
252 | } | ||
253 | |||
254 | attrs.va_mode = mode; | ||
255 | error = venus_mkdir(dir->i_sb, coda_i2f(dir), | ||
256 | name, len, &newfid, &attrs); | ||
257 | |||
258 | if ( error ) { | ||
259 | unlock_kernel(); | ||
260 | d_drop(de); | ||
261 | return error; | ||
262 | } | ||
263 | |||
264 | inode = coda_iget(dir->i_sb, &newfid, &attrs); | ||
265 | if ( IS_ERR(inode) ) { | ||
266 | unlock_kernel(); | ||
267 | d_drop(de); | ||
268 | return PTR_ERR(inode); | ||
269 | } | ||
270 | |||
271 | /* invalidate the directory cnode's attributes */ | ||
272 | coda_dir_changed(dir, 1); | ||
273 | unlock_kernel(); | ||
274 | d_instantiate(de, inode); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | /* try to make de an entry in dir_inodde linked to source_de */ | ||
279 | static int coda_link(struct dentry *source_de, struct inode *dir_inode, | ||
280 | struct dentry *de) | ||
281 | { | ||
282 | struct inode *inode = source_de->d_inode; | ||
283 | const char * name = de->d_name.name; | ||
284 | int len = de->d_name.len; | ||
285 | int error; | ||
286 | |||
287 | lock_kernel(); | ||
288 | coda_vfs_stat.link++; | ||
289 | |||
290 | if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { | ||
291 | unlock_kernel(); | ||
292 | return -EPERM; | ||
293 | } | ||
294 | |||
295 | error = venus_link(dir_inode->i_sb, coda_i2f(inode), | ||
296 | coda_i2f(dir_inode), (const char *)name, len); | ||
297 | |||
298 | if (error) { | ||
299 | d_drop(de); | ||
300 | goto out; | ||
301 | } | ||
302 | |||
303 | coda_dir_changed(dir_inode, 0); | ||
304 | atomic_inc(&inode->i_count); | ||
305 | d_instantiate(de, inode); | ||
306 | inode->i_nlink++; | ||
307 | |||
308 | out: | ||
309 | unlock_kernel(); | ||
310 | return(error); | ||
311 | } | ||
312 | |||
313 | |||
314 | static int coda_symlink(struct inode *dir_inode, struct dentry *de, | ||
315 | const char *symname) | ||
316 | { | ||
317 | const char *name = de->d_name.name; | ||
318 | int len = de->d_name.len; | ||
319 | int symlen; | ||
320 | int error=0; | ||
321 | |||
322 | lock_kernel(); | ||
323 | coda_vfs_stat.symlink++; | ||
324 | |||
325 | if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { | ||
326 | unlock_kernel(); | ||
327 | return -EPERM; | ||
328 | } | ||
329 | |||
330 | symlen = strlen(symname); | ||
331 | if ( symlen > CODA_MAXPATHLEN ) { | ||
332 | unlock_kernel(); | ||
333 | return -ENAMETOOLONG; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * This entry is now negative. Since we do not create | ||
338 | * an inode for the entry we have to drop it. | ||
339 | */ | ||
340 | d_drop(de); | ||
341 | error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len, | ||
342 | symname, symlen); | ||
343 | |||
344 | /* mtime is no good anymore */ | ||
345 | if ( !error ) | ||
346 | coda_dir_changed(dir_inode, 0); | ||
347 | |||
348 | unlock_kernel(); | ||
349 | return error; | ||
350 | } | ||
351 | |||
352 | /* destruction routines: unlink, rmdir */ | ||
353 | int coda_unlink(struct inode *dir, struct dentry *de) | ||
354 | { | ||
355 | int error; | ||
356 | const char *name = de->d_name.name; | ||
357 | int len = de->d_name.len; | ||
358 | |||
359 | lock_kernel(); | ||
360 | coda_vfs_stat.unlink++; | ||
361 | |||
362 | error = venus_remove(dir->i_sb, coda_i2f(dir), name, len); | ||
363 | if ( error ) { | ||
364 | unlock_kernel(); | ||
365 | return error; | ||
366 | } | ||
367 | |||
368 | coda_dir_changed(dir, 0); | ||
369 | de->d_inode->i_nlink--; | ||
370 | unlock_kernel(); | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | int coda_rmdir(struct inode *dir, struct dentry *de) | ||
376 | { | ||
377 | const char *name = de->d_name.name; | ||
378 | int len = de->d_name.len; | ||
379 | int error; | ||
380 | |||
381 | lock_kernel(); | ||
382 | coda_vfs_stat.rmdir++; | ||
383 | |||
384 | if (!d_unhashed(de)) { | ||
385 | unlock_kernel(); | ||
386 | return -EBUSY; | ||
387 | } | ||
388 | error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len); | ||
389 | |||
390 | if ( error ) { | ||
391 | unlock_kernel(); | ||
392 | return error; | ||
393 | } | ||
394 | |||
395 | coda_dir_changed(dir, -1); | ||
396 | de->d_inode->i_nlink--; | ||
397 | d_delete(de); | ||
398 | unlock_kernel(); | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | /* rename */ | ||
404 | static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
405 | struct inode *new_dir, struct dentry *new_dentry) | ||
406 | { | ||
407 | const char *old_name = old_dentry->d_name.name; | ||
408 | const char *new_name = new_dentry->d_name.name; | ||
409 | int old_length = old_dentry->d_name.len; | ||
410 | int new_length = new_dentry->d_name.len; | ||
411 | int link_adjust = 0; | ||
412 | int error; | ||
413 | |||
414 | lock_kernel(); | ||
415 | coda_vfs_stat.rename++; | ||
416 | |||
417 | error = venus_rename(old_dir->i_sb, coda_i2f(old_dir), | ||
418 | coda_i2f(new_dir), old_length, new_length, | ||
419 | (const char *) old_name, (const char *)new_name); | ||
420 | |||
421 | if ( !error ) { | ||
422 | if ( new_dentry->d_inode ) { | ||
423 | if ( S_ISDIR(new_dentry->d_inode->i_mode) ) | ||
424 | link_adjust = 1; | ||
425 | |||
426 | coda_dir_changed(old_dir, -link_adjust); | ||
427 | coda_dir_changed(new_dir, link_adjust); | ||
428 | coda_flag_inode(new_dentry->d_inode, C_VATTR); | ||
429 | } else { | ||
430 | coda_flag_inode(old_dir, C_VATTR); | ||
431 | coda_flag_inode(new_dir, C_VATTR); | ||
432 | } | ||
433 | } | ||
434 | unlock_kernel(); | ||
435 | |||
436 | return error; | ||
437 | } | ||
438 | |||
439 | |||
440 | /* file operations for directories */ | ||
441 | int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir) | ||
442 | { | ||
443 | struct dentry *coda_dentry = coda_file->f_dentry; | ||
444 | struct coda_file_info *cfi; | ||
445 | struct file *host_file; | ||
446 | struct inode *host_inode; | ||
447 | int ret; | ||
448 | |||
449 | cfi = CODA_FTOC(coda_file); | ||
450 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
451 | host_file = cfi->cfi_container; | ||
452 | |||
453 | coda_vfs_stat.readdir++; | ||
454 | |||
455 | host_inode = host_file->f_dentry->d_inode; | ||
456 | down(&host_inode->i_sem); | ||
457 | host_file->f_pos = coda_file->f_pos; | ||
458 | |||
459 | if (!host_file->f_op->readdir) { | ||
460 | /* Venus: we must read Venus dirents from the file */ | ||
461 | ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry); | ||
462 | } else { | ||
463 | /* potemkin case: we were handed a directory inode. */ | ||
464 | /* Yuk, we can't call vfs_readdir because we are already | ||
465 | * holding the inode semaphore. */ | ||
466 | ret = -ENOTDIR; | ||
467 | if (!host_file->f_op || !host_file->f_op->readdir) | ||
468 | goto out; | ||
469 | |||
470 | ret = -ENOENT; | ||
471 | if (!IS_DEADDIR(host_inode)) { | ||
472 | ret = host_file->f_op->readdir(host_file, filldir, dirent); | ||
473 | file_accessed(host_file); | ||
474 | } | ||
475 | } | ||
476 | out: | ||
477 | coda_file->f_pos = host_file->f_pos; | ||
478 | up(&host_inode->i_sem); | ||
479 | |||
480 | return ret; | ||
481 | } | ||
482 | |||
483 | static inline unsigned int CDT2DT(unsigned char cdt) | ||
484 | { | ||
485 | unsigned int dt; | ||
486 | |||
487 | switch(cdt) { | ||
488 | case CDT_UNKNOWN: dt = DT_UNKNOWN; break; | ||
489 | case CDT_FIFO: dt = DT_FIFO; break; | ||
490 | case CDT_CHR: dt = DT_CHR; break; | ||
491 | case CDT_DIR: dt = DT_DIR; break; | ||
492 | case CDT_BLK: dt = DT_BLK; break; | ||
493 | case CDT_REG: dt = DT_REG; break; | ||
494 | case CDT_LNK: dt = DT_LNK; break; | ||
495 | case CDT_SOCK: dt = DT_SOCK; break; | ||
496 | case CDT_WHT: dt = DT_WHT; break; | ||
497 | default: dt = DT_UNKNOWN; break; | ||
498 | } | ||
499 | return dt; | ||
500 | } | ||
501 | |||
502 | /* support routines */ | ||
503 | static int coda_venus_readdir(struct file *filp, filldir_t filldir, | ||
504 | void *dirent, struct dentry *dir) | ||
505 | { | ||
506 | int result = 0; /* # of entries returned */ | ||
507 | struct venus_dirent *vdir; | ||
508 | unsigned long vdir_size = | ||
509 | (unsigned long)(&((struct venus_dirent *)0)->d_name); | ||
510 | unsigned int type; | ||
511 | struct qstr name; | ||
512 | ino_t ino; | ||
513 | int ret, i; | ||
514 | |||
515 | vdir = (struct venus_dirent *)kmalloc(sizeof(*vdir), GFP_KERNEL); | ||
516 | if (!vdir) return -ENOMEM; | ||
517 | |||
518 | i = filp->f_pos; | ||
519 | switch(i) { | ||
520 | case 0: | ||
521 | ret = filldir(dirent, ".", 1, 0, dir->d_inode->i_ino, DT_DIR); | ||
522 | if (ret < 0) break; | ||
523 | result++; | ||
524 | filp->f_pos++; | ||
525 | /* fallthrough */ | ||
526 | case 1: | ||
527 | ret = filldir(dirent, "..", 2, 1, dir->d_parent->d_inode->i_ino, DT_DIR); | ||
528 | if (ret < 0) break; | ||
529 | result++; | ||
530 | filp->f_pos++; | ||
531 | /* fallthrough */ | ||
532 | default: | ||
533 | while (1) { | ||
534 | /* read entries from the directory file */ | ||
535 | ret = kernel_read(filp, filp->f_pos - 2, (char *)vdir, | ||
536 | sizeof(*vdir)); | ||
537 | if (ret < 0) { | ||
538 | printk("coda_venus_readdir: read dir failed %d\n", ret); | ||
539 | break; | ||
540 | } | ||
541 | if (ret == 0) break; /* end of directory file reached */ | ||
542 | |||
543 | /* catch truncated reads */ | ||
544 | if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) { | ||
545 | printk("coda_venus_readdir: short read: %ld\n", | ||
546 | filp->f_dentry->d_inode->i_ino); | ||
547 | ret = -EBADF; | ||
548 | break; | ||
549 | } | ||
550 | /* validate whether the directory file actually makes sense */ | ||
551 | if (vdir->d_reclen < vdir_size + vdir->d_namlen) { | ||
552 | printk("coda_venus_readdir: Invalid dir: %ld\n", | ||
553 | filp->f_dentry->d_inode->i_ino); | ||
554 | ret = -EBADF; | ||
555 | break; | ||
556 | } | ||
557 | |||
558 | name.len = vdir->d_namlen; | ||
559 | name.name = vdir->d_name; | ||
560 | |||
561 | /* Make sure we skip '.' and '..', we already got those */ | ||
562 | if (name.name[0] == '.' && (name.len == 1 || | ||
563 | (vdir->d_name[1] == '.' && name.len == 2))) | ||
564 | vdir->d_fileno = name.len = 0; | ||
565 | |||
566 | /* skip null entries */ | ||
567 | if (vdir->d_fileno && name.len) { | ||
568 | /* try to look up this entry in the dcache, that way | ||
569 | * userspace doesn't have to worry about breaking | ||
570 | * getcwd by having mismatched inode numbers for | ||
571 | * internal volume mountpoints. */ | ||
572 | ino = find_inode_number(dir, &name); | ||
573 | if (!ino) ino = vdir->d_fileno; | ||
574 | |||
575 | type = CDT2DT(vdir->d_type); | ||
576 | ret = filldir(dirent, name.name, name.len, filp->f_pos, | ||
577 | ino, type); | ||
578 | /* failure means no space for filling in this round */ | ||
579 | if (ret < 0) break; | ||
580 | result++; | ||
581 | } | ||
582 | /* we'll always have progress because d_reclen is unsigned and | ||
583 | * we've already established it is non-zero. */ | ||
584 | filp->f_pos += vdir->d_reclen; | ||
585 | } | ||
586 | } | ||
587 | kfree(vdir); | ||
588 | return result ? result : ret; | ||
589 | } | ||
590 | |||
591 | /* called when a cache lookup succeeds */ | ||
592 | static int coda_dentry_revalidate(struct dentry *de, struct nameidata *nd) | ||
593 | { | ||
594 | struct inode *inode = de->d_inode; | ||
595 | struct coda_inode_info *cii; | ||
596 | |||
597 | if (!inode) | ||
598 | return 1; | ||
599 | lock_kernel(); | ||
600 | if (coda_isroot(inode)) | ||
601 | goto out; | ||
602 | if (is_bad_inode(inode)) | ||
603 | goto bad; | ||
604 | |||
605 | cii = ITOC(de->d_inode); | ||
606 | if (!(cii->c_flags & (C_PURGE | C_FLUSH))) | ||
607 | goto out; | ||
608 | |||
609 | shrink_dcache_parent(de); | ||
610 | |||
611 | /* propagate for a flush */ | ||
612 | if (cii->c_flags & C_FLUSH) | ||
613 | coda_flag_inode_children(inode, C_FLUSH); | ||
614 | |||
615 | if (atomic_read(&de->d_count) > 1) | ||
616 | /* pretend it's valid, but don't change the flags */ | ||
617 | goto out; | ||
618 | |||
619 | /* clear the flags. */ | ||
620 | cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); | ||
621 | |||
622 | bad: | ||
623 | unlock_kernel(); | ||
624 | return 0; | ||
625 | out: | ||
626 | unlock_kernel(); | ||
627 | return 1; | ||
628 | } | ||
629 | |||
630 | /* | ||
631 | * This is the callback from dput() when d_count is going to 0. | ||
632 | * We use this to unhash dentries with bad inodes. | ||
633 | */ | ||
634 | static int coda_dentry_delete(struct dentry * dentry) | ||
635 | { | ||
636 | int flags; | ||
637 | |||
638 | if (!dentry->d_inode) | ||
639 | return 0; | ||
640 | |||
641 | flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE; | ||
642 | if (is_bad_inode(dentry->d_inode) || flags) { | ||
643 | return 1; | ||
644 | } | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | |||
649 | |||
650 | /* | ||
651 | * This is called when we want to check if the inode has | ||
652 | * changed on the server. Coda makes this easy since the | ||
653 | * cache manager Venus issues a downcall to the kernel when this | ||
654 | * happens | ||
655 | */ | ||
656 | int coda_revalidate_inode(struct dentry *dentry) | ||
657 | { | ||
658 | struct coda_vattr attr; | ||
659 | int error = 0; | ||
660 | int old_mode; | ||
661 | ino_t old_ino; | ||
662 | struct inode *inode = dentry->d_inode; | ||
663 | struct coda_inode_info *cii = ITOC(inode); | ||
664 | |||
665 | lock_kernel(); | ||
666 | if ( !cii->c_flags ) | ||
667 | goto ok; | ||
668 | |||
669 | if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) { | ||
670 | error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr); | ||
671 | if ( error ) | ||
672 | goto return_bad; | ||
673 | |||
674 | /* this inode may be lost if: | ||
675 | - it's ino changed | ||
676 | - type changes must be permitted for repair and | ||
677 | missing mount points. | ||
678 | */ | ||
679 | old_mode = inode->i_mode; | ||
680 | old_ino = inode->i_ino; | ||
681 | coda_vattr_to_iattr(inode, &attr); | ||
682 | |||
683 | if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) { | ||
684 | printk("Coda: inode %ld, fid %s changed type!\n", | ||
685 | inode->i_ino, coda_f2s(&(cii->c_fid))); | ||
686 | } | ||
687 | |||
688 | /* the following can happen when a local fid is replaced | ||
689 | with a global one, here we lose and declare the inode bad */ | ||
690 | if (inode->i_ino != old_ino) | ||
691 | goto return_bad; | ||
692 | |||
693 | coda_flag_inode_children(inode, C_FLUSH); | ||
694 | cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH); | ||
695 | } | ||
696 | |||
697 | ok: | ||
698 | unlock_kernel(); | ||
699 | return 0; | ||
700 | |||
701 | return_bad: | ||
702 | unlock_kernel(); | ||
703 | return -EIO; | ||
704 | } | ||
diff --git a/fs/coda/file.c b/fs/coda/file.c new file mode 100644 index 000000000000..e6bc022568f3 --- /dev/null +++ b/fs/coda/file.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | * File operations for Coda. | ||
3 | * Original version: (C) 1996 Peter Braam | ||
4 | * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University | ||
5 | * | ||
6 | * Carnegie Mellon encourages users of this code to contribute improvements | ||
7 | * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>. | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/time.h> | ||
13 | #include <linux/file.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/stat.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/smp_lock.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | |||
21 | #include <linux/coda.h> | ||
22 | #include <linux/coda_linux.h> | ||
23 | #include <linux/coda_fs_i.h> | ||
24 | #include <linux/coda_psdev.h> | ||
25 | #include <linux/coda_proc.h> | ||
26 | |||
27 | /* if CODA_STORE fails with EOPNOTSUPP, venus clearly doesn't support | ||
28 | * CODA_STORE/CODA_RELEASE and we fall back on using the CODA_CLOSE upcall */ | ||
29 | static int use_coda_close; | ||
30 | |||
31 | static ssize_t | ||
32 | coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *ppos) | ||
33 | { | ||
34 | struct coda_file_info *cfi; | ||
35 | struct file *host_file; | ||
36 | |||
37 | cfi = CODA_FTOC(coda_file); | ||
38 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
39 | host_file = cfi->cfi_container; | ||
40 | |||
41 | if (!host_file->f_op || !host_file->f_op->read) | ||
42 | return -EINVAL; | ||
43 | |||
44 | return host_file->f_op->read(host_file, buf, count, ppos); | ||
45 | } | ||
46 | |||
47 | static ssize_t | ||
48 | coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count, | ||
49 | read_actor_t actor, void *target) | ||
50 | { | ||
51 | struct coda_file_info *cfi; | ||
52 | struct file *host_file; | ||
53 | |||
54 | cfi = CODA_FTOC(coda_file); | ||
55 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
56 | host_file = cfi->cfi_container; | ||
57 | |||
58 | if (!host_file->f_op || !host_file->f_op->sendfile) | ||
59 | return -EINVAL; | ||
60 | |||
61 | return host_file->f_op->sendfile(host_file, ppos, count, actor, target); | ||
62 | } | ||
63 | |||
64 | static ssize_t | ||
65 | coda_file_write(struct file *coda_file, const char __user *buf, size_t count, loff_t *ppos) | ||
66 | { | ||
67 | struct inode *host_inode, *coda_inode = coda_file->f_dentry->d_inode; | ||
68 | struct coda_file_info *cfi; | ||
69 | struct file *host_file; | ||
70 | ssize_t ret; | ||
71 | |||
72 | cfi = CODA_FTOC(coda_file); | ||
73 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
74 | host_file = cfi->cfi_container; | ||
75 | |||
76 | if (!host_file->f_op || !host_file->f_op->write) | ||
77 | return -EINVAL; | ||
78 | |||
79 | host_inode = host_file->f_dentry->d_inode; | ||
80 | down(&coda_inode->i_sem); | ||
81 | |||
82 | ret = host_file->f_op->write(host_file, buf, count, ppos); | ||
83 | |||
84 | coda_inode->i_size = host_inode->i_size; | ||
85 | coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9; | ||
86 | coda_inode->i_mtime = coda_inode->i_ctime = CURRENT_TIME_SEC; | ||
87 | up(&coda_inode->i_sem); | ||
88 | |||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | static int | ||
93 | coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma) | ||
94 | { | ||
95 | struct coda_file_info *cfi; | ||
96 | struct coda_inode_info *cii; | ||
97 | struct file *host_file; | ||
98 | struct inode *coda_inode, *host_inode; | ||
99 | |||
100 | cfi = CODA_FTOC(coda_file); | ||
101 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
102 | host_file = cfi->cfi_container; | ||
103 | |||
104 | if (!host_file->f_op || !host_file->f_op->mmap) | ||
105 | return -ENODEV; | ||
106 | |||
107 | coda_inode = coda_file->f_dentry->d_inode; | ||
108 | host_inode = host_file->f_dentry->d_inode; | ||
109 | coda_file->f_mapping = host_file->f_mapping; | ||
110 | if (coda_inode->i_mapping == &coda_inode->i_data) | ||
111 | coda_inode->i_mapping = host_inode->i_mapping; | ||
112 | |||
113 | /* only allow additional mmaps as long as userspace isn't changing | ||
114 | * the container file on us! */ | ||
115 | else if (coda_inode->i_mapping != host_inode->i_mapping) | ||
116 | return -EBUSY; | ||
117 | |||
118 | /* keep track of how often the coda_inode/host_file has been mmapped */ | ||
119 | cii = ITOC(coda_inode); | ||
120 | cii->c_mapcount++; | ||
121 | cfi->cfi_mapcount++; | ||
122 | |||
123 | return host_file->f_op->mmap(host_file, vma); | ||
124 | } | ||
125 | |||
126 | int coda_open(struct inode *coda_inode, struct file *coda_file) | ||
127 | { | ||
128 | struct file *host_file = NULL; | ||
129 | int error; | ||
130 | unsigned short flags = coda_file->f_flags & (~O_EXCL); | ||
131 | unsigned short coda_flags = coda_flags_to_cflags(flags); | ||
132 | struct coda_file_info *cfi; | ||
133 | |||
134 | coda_vfs_stat.open++; | ||
135 | |||
136 | cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL); | ||
137 | if (!cfi) { | ||
138 | unlock_kernel(); | ||
139 | return -ENOMEM; | ||
140 | } | ||
141 | |||
142 | lock_kernel(); | ||
143 | |||
144 | error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, | ||
145 | &host_file); | ||
146 | if (error || !host_file) { | ||
147 | kfree(cfi); | ||
148 | unlock_kernel(); | ||
149 | return error; | ||
150 | } | ||
151 | |||
152 | host_file->f_flags |= coda_file->f_flags & (O_APPEND | O_SYNC); | ||
153 | |||
154 | cfi->cfi_magic = CODA_MAGIC; | ||
155 | cfi->cfi_mapcount = 0; | ||
156 | cfi->cfi_container = host_file; | ||
157 | |||
158 | BUG_ON(coda_file->private_data != NULL); | ||
159 | coda_file->private_data = cfi; | ||
160 | |||
161 | unlock_kernel(); | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | int coda_flush(struct file *coda_file) | ||
166 | { | ||
167 | unsigned short flags = coda_file->f_flags & ~O_EXCL; | ||
168 | unsigned short coda_flags = coda_flags_to_cflags(flags); | ||
169 | struct coda_file_info *cfi; | ||
170 | struct inode *coda_inode; | ||
171 | int err = 0, fcnt; | ||
172 | |||
173 | lock_kernel(); | ||
174 | |||
175 | coda_vfs_stat.flush++; | ||
176 | |||
177 | /* last close semantics */ | ||
178 | fcnt = file_count(coda_file); | ||
179 | if (fcnt > 1) | ||
180 | goto out; | ||
181 | |||
182 | /* No need to make an upcall when we have not made any modifications | ||
183 | * to the file */ | ||
184 | if ((coda_file->f_flags & O_ACCMODE) == O_RDONLY) | ||
185 | goto out; | ||
186 | |||
187 | if (use_coda_close) | ||
188 | goto out; | ||
189 | |||
190 | cfi = CODA_FTOC(coda_file); | ||
191 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
192 | |||
193 | coda_inode = coda_file->f_dentry->d_inode; | ||
194 | |||
195 | err = venus_store(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags, | ||
196 | coda_file->f_uid); | ||
197 | |||
198 | if (err == -EOPNOTSUPP) { | ||
199 | use_coda_close = 1; | ||
200 | err = 0; | ||
201 | } | ||
202 | |||
203 | out: | ||
204 | unlock_kernel(); | ||
205 | return err; | ||
206 | } | ||
207 | |||
208 | int coda_release(struct inode *coda_inode, struct file *coda_file) | ||
209 | { | ||
210 | unsigned short flags = (coda_file->f_flags) & (~O_EXCL); | ||
211 | unsigned short coda_flags = coda_flags_to_cflags(flags); | ||
212 | struct coda_file_info *cfi; | ||
213 | struct coda_inode_info *cii; | ||
214 | struct inode *host_inode; | ||
215 | int err = 0; | ||
216 | |||
217 | lock_kernel(); | ||
218 | coda_vfs_stat.release++; | ||
219 | |||
220 | if (!use_coda_close) { | ||
221 | err = venus_release(coda_inode->i_sb, coda_i2f(coda_inode), | ||
222 | coda_flags); | ||
223 | if (err == -EOPNOTSUPP) { | ||
224 | use_coda_close = 1; | ||
225 | err = 0; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | cfi = CODA_FTOC(coda_file); | ||
230 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
231 | |||
232 | if (use_coda_close) | ||
233 | err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode), | ||
234 | coda_flags, coda_file->f_uid); | ||
235 | |||
236 | host_inode = cfi->cfi_container->f_dentry->d_inode; | ||
237 | cii = ITOC(coda_inode); | ||
238 | |||
239 | /* did we mmap this file? */ | ||
240 | if (coda_inode->i_mapping == &host_inode->i_data) { | ||
241 | cii->c_mapcount -= cfi->cfi_mapcount; | ||
242 | if (!cii->c_mapcount) | ||
243 | coda_inode->i_mapping = &coda_inode->i_data; | ||
244 | } | ||
245 | |||
246 | fput(cfi->cfi_container); | ||
247 | kfree(coda_file->private_data); | ||
248 | coda_file->private_data = NULL; | ||
249 | |||
250 | unlock_kernel(); | ||
251 | return err; | ||
252 | } | ||
253 | |||
254 | int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) | ||
255 | { | ||
256 | struct file *host_file; | ||
257 | struct dentry *host_dentry; | ||
258 | struct inode *host_inode, *coda_inode = coda_dentry->d_inode; | ||
259 | struct coda_file_info *cfi; | ||
260 | int err = 0; | ||
261 | |||
262 | if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) || | ||
263 | S_ISLNK(coda_inode->i_mode))) | ||
264 | return -EINVAL; | ||
265 | |||
266 | cfi = CODA_FTOC(coda_file); | ||
267 | BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); | ||
268 | host_file = cfi->cfi_container; | ||
269 | |||
270 | coda_vfs_stat.fsync++; | ||
271 | |||
272 | if (host_file->f_op && host_file->f_op->fsync) { | ||
273 | host_dentry = host_file->f_dentry; | ||
274 | host_inode = host_dentry->d_inode; | ||
275 | down(&host_inode->i_sem); | ||
276 | err = host_file->f_op->fsync(host_file, host_dentry, datasync); | ||
277 | up(&host_inode->i_sem); | ||
278 | } | ||
279 | |||
280 | if ( !err && !datasync ) { | ||
281 | lock_kernel(); | ||
282 | err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode)); | ||
283 | unlock_kernel(); | ||
284 | } | ||
285 | |||
286 | return err; | ||
287 | } | ||
288 | |||
289 | struct file_operations coda_file_operations = { | ||
290 | .llseek = generic_file_llseek, | ||
291 | .read = coda_file_read, | ||
292 | .write = coda_file_write, | ||
293 | .mmap = coda_file_mmap, | ||
294 | .open = coda_open, | ||
295 | .flush = coda_flush, | ||
296 | .release = coda_release, | ||
297 | .fsync = coda_fsync, | ||
298 | .sendfile = coda_file_sendfile, | ||
299 | }; | ||
300 | |||
diff --git a/fs/coda/inode.c b/fs/coda/inode.c new file mode 100644 index 000000000000..04a73fb4848f --- /dev/null +++ b/fs/coda/inode.c | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * Super block/filesystem wide operations | ||
3 | * | ||
4 | * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and | ||
5 | * Michael Callahan <callahan@maths.ox.ac.uk> | ||
6 | * | ||
7 | * Rewritten for Linux 2.1. Peter Braam <braam@cs.cmu.edu> | ||
8 | * Copyright (C) Carnegie Mellon University | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/mm.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/stat.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/unistd.h> | ||
18 | #include <linux/smp_lock.h> | ||
19 | #include <linux/file.h> | ||
20 | #include <linux/vfs.h> | ||
21 | |||
22 | #include <asm/system.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | |||
25 | #include <linux/fs.h> | ||
26 | #include <linux/vmalloc.h> | ||
27 | |||
28 | #include <linux/coda.h> | ||
29 | #include <linux/coda_linux.h> | ||
30 | #include <linux/coda_psdev.h> | ||
31 | #include <linux/coda_fs_i.h> | ||
32 | #include <linux/coda_cache.h> | ||
33 | |||
34 | /* VFS super_block ops */ | ||
35 | static void coda_clear_inode(struct inode *); | ||
36 | static void coda_put_super(struct super_block *); | ||
37 | static int coda_statfs(struct super_block *sb, struct kstatfs *buf); | ||
38 | |||
39 | static kmem_cache_t * coda_inode_cachep; | ||
40 | |||
41 | static struct inode *coda_alloc_inode(struct super_block *sb) | ||
42 | { | ||
43 | struct coda_inode_info *ei; | ||
44 | ei = (struct coda_inode_info *)kmem_cache_alloc(coda_inode_cachep, SLAB_KERNEL); | ||
45 | if (!ei) | ||
46 | return NULL; | ||
47 | memset(&ei->c_fid, 0, sizeof(struct CodaFid)); | ||
48 | ei->c_flags = 0; | ||
49 | ei->c_uid = 0; | ||
50 | ei->c_cached_perm = 0; | ||
51 | return &ei->vfs_inode; | ||
52 | } | ||
53 | |||
54 | static void coda_destroy_inode(struct inode *inode) | ||
55 | { | ||
56 | kmem_cache_free(coda_inode_cachep, ITOC(inode)); | ||
57 | } | ||
58 | |||
59 | static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) | ||
60 | { | ||
61 | struct coda_inode_info *ei = (struct coda_inode_info *) foo; | ||
62 | |||
63 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | ||
64 | SLAB_CTOR_CONSTRUCTOR) | ||
65 | inode_init_once(&ei->vfs_inode); | ||
66 | } | ||
67 | |||
68 | int coda_init_inodecache(void) | ||
69 | { | ||
70 | coda_inode_cachep = kmem_cache_create("coda_inode_cache", | ||
71 | sizeof(struct coda_inode_info), | ||
72 | 0, SLAB_RECLAIM_ACCOUNT, | ||
73 | init_once, NULL); | ||
74 | if (coda_inode_cachep == NULL) | ||
75 | return -ENOMEM; | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | void coda_destroy_inodecache(void) | ||
80 | { | ||
81 | if (kmem_cache_destroy(coda_inode_cachep)) | ||
82 | printk(KERN_INFO "coda_inode_cache: not all structures were freed\n"); | ||
83 | } | ||
84 | |||
85 | static int coda_remount(struct super_block *sb, int *flags, char *data) | ||
86 | { | ||
87 | *flags |= MS_NODIRATIME; | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | /* exported operations */ | ||
92 | static struct super_operations coda_super_operations = | ||
93 | { | ||
94 | .alloc_inode = coda_alloc_inode, | ||
95 | .destroy_inode = coda_destroy_inode, | ||
96 | .clear_inode = coda_clear_inode, | ||
97 | .put_super = coda_put_super, | ||
98 | .statfs = coda_statfs, | ||
99 | .remount_fs = coda_remount, | ||
100 | }; | ||
101 | |||
102 | static int get_device_index(struct coda_mount_data *data) | ||
103 | { | ||
104 | struct file *file; | ||
105 | struct inode *inode; | ||
106 | int idx; | ||
107 | |||
108 | if(data == NULL) { | ||
109 | printk("coda_read_super: Bad mount data\n"); | ||
110 | return -1; | ||
111 | } | ||
112 | |||
113 | if(data->version != CODA_MOUNT_VERSION) { | ||
114 | printk("coda_read_super: Bad mount version\n"); | ||
115 | return -1; | ||
116 | } | ||
117 | |||
118 | file = fget(data->fd); | ||
119 | inode = NULL; | ||
120 | if(file) | ||
121 | inode = file->f_dentry->d_inode; | ||
122 | |||
123 | if(!inode || !S_ISCHR(inode->i_mode) || | ||
124 | imajor(inode) != CODA_PSDEV_MAJOR) { | ||
125 | if(file) | ||
126 | fput(file); | ||
127 | |||
128 | printk("coda_read_super: Bad file\n"); | ||
129 | return -1; | ||
130 | } | ||
131 | |||
132 | idx = iminor(inode); | ||
133 | fput(file); | ||
134 | |||
135 | if(idx < 0 || idx >= MAX_CODADEVS) { | ||
136 | printk("coda_read_super: Bad minor number\n"); | ||
137 | return -1; | ||
138 | } | ||
139 | |||
140 | return idx; | ||
141 | } | ||
142 | |||
143 | static int coda_fill_super(struct super_block *sb, void *data, int silent) | ||
144 | { | ||
145 | struct inode *root = NULL; | ||
146 | struct coda_sb_info *sbi = NULL; | ||
147 | struct venus_comm *vc = NULL; | ||
148 | struct CodaFid fid; | ||
149 | int error; | ||
150 | int idx; | ||
151 | |||
152 | idx = get_device_index((struct coda_mount_data *) data); | ||
153 | |||
154 | /* Ignore errors in data, for backward compatibility */ | ||
155 | if(idx == -1) | ||
156 | idx = 0; | ||
157 | |||
158 | printk(KERN_INFO "coda_read_super: device index: %i\n", idx); | ||
159 | |||
160 | vc = &coda_comms[idx]; | ||
161 | if (!vc->vc_inuse) { | ||
162 | printk("coda_read_super: No pseudo device\n"); | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | |||
166 | if ( vc->vc_sb ) { | ||
167 | printk("coda_read_super: Device already mounted\n"); | ||
168 | return -EBUSY; | ||
169 | } | ||
170 | |||
171 | sbi = kmalloc(sizeof(struct coda_sb_info), GFP_KERNEL); | ||
172 | if(!sbi) { | ||
173 | return -ENOMEM; | ||
174 | } | ||
175 | |||
176 | vc->vc_sb = sb; | ||
177 | |||
178 | sbi->sbi_vcomm = vc; | ||
179 | |||
180 | sb->s_fs_info = sbi; | ||
181 | sb->s_flags |= MS_NODIRATIME; /* probably even noatime */ | ||
182 | sb->s_blocksize = 1024; /* XXXXX what do we put here?? */ | ||
183 | sb->s_blocksize_bits = 10; | ||
184 | sb->s_magic = CODA_SUPER_MAGIC; | ||
185 | sb->s_op = &coda_super_operations; | ||
186 | |||
187 | /* get root fid from Venus: this needs the root inode */ | ||
188 | error = venus_rootfid(sb, &fid); | ||
189 | if ( error ) { | ||
190 | printk("coda_read_super: coda_get_rootfid failed with %d\n", | ||
191 | error); | ||
192 | goto error; | ||
193 | } | ||
194 | printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid)); | ||
195 | |||
196 | /* make root inode */ | ||
197 | error = coda_cnode_make(&root, &fid, sb); | ||
198 | if ( error || !root ) { | ||
199 | printk("Failure of coda_cnode_make for root: error %d\n", error); | ||
200 | goto error; | ||
201 | } | ||
202 | |||
203 | printk("coda_read_super: rootinode is %ld dev %s\n", | ||
204 | root->i_ino, root->i_sb->s_id); | ||
205 | sb->s_root = d_alloc_root(root); | ||
206 | if (!sb->s_root) | ||
207 | goto error; | ||
208 | return 0; | ||
209 | |||
210 | error: | ||
211 | if (sbi) { | ||
212 | kfree(sbi); | ||
213 | if(vc) | ||
214 | vc->vc_sb = NULL; | ||
215 | } | ||
216 | if (root) | ||
217 | iput(root); | ||
218 | |||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | static void coda_put_super(struct super_block *sb) | ||
223 | { | ||
224 | struct coda_sb_info *sbi; | ||
225 | |||
226 | sbi = coda_sbp(sb); | ||
227 | sbi->sbi_vcomm->vc_sb = NULL; | ||
228 | |||
229 | printk("Coda: Bye bye.\n"); | ||
230 | kfree(sbi); | ||
231 | } | ||
232 | |||
233 | static void coda_clear_inode(struct inode *inode) | ||
234 | { | ||
235 | coda_cache_clear_inode(inode); | ||
236 | } | ||
237 | |||
238 | int coda_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) | ||
239 | { | ||
240 | int err = coda_revalidate_inode(dentry); | ||
241 | if (!err) | ||
242 | generic_fillattr(dentry->d_inode, stat); | ||
243 | return err; | ||
244 | } | ||
245 | |||
246 | int coda_setattr(struct dentry *de, struct iattr *iattr) | ||
247 | { | ||
248 | struct inode *inode = de->d_inode; | ||
249 | struct coda_vattr vattr; | ||
250 | int error; | ||
251 | |||
252 | lock_kernel(); | ||
253 | |||
254 | memset(&vattr, 0, sizeof(vattr)); | ||
255 | |||
256 | inode->i_ctime = CURRENT_TIME_SEC; | ||
257 | coda_iattr_to_vattr(iattr, &vattr); | ||
258 | vattr.va_type = C_VNON; /* cannot set type */ | ||
259 | |||
260 | /* Venus is responsible for truncating the container-file!!! */ | ||
261 | error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr); | ||
262 | |||
263 | if ( !error ) { | ||
264 | coda_vattr_to_iattr(inode, &vattr); | ||
265 | coda_cache_clear_inode(inode); | ||
266 | } | ||
267 | |||
268 | unlock_kernel(); | ||
269 | |||
270 | return error; | ||
271 | } | ||
272 | |||
273 | struct inode_operations coda_file_inode_operations = { | ||
274 | .permission = coda_permission, | ||
275 | .getattr = coda_getattr, | ||
276 | .setattr = coda_setattr, | ||
277 | }; | ||
278 | |||
279 | static int coda_statfs(struct super_block *sb, struct kstatfs *buf) | ||
280 | { | ||
281 | int error; | ||
282 | |||
283 | lock_kernel(); | ||
284 | |||
285 | error = venus_statfs(sb, buf); | ||
286 | |||
287 | unlock_kernel(); | ||
288 | |||
289 | if (error) { | ||
290 | /* fake something like AFS does */ | ||
291 | buf->f_blocks = 9000000; | ||
292 | buf->f_bfree = 9000000; | ||
293 | buf->f_bavail = 9000000; | ||
294 | buf->f_files = 9000000; | ||
295 | buf->f_ffree = 9000000; | ||
296 | } | ||
297 | |||
298 | /* and fill in the rest */ | ||
299 | buf->f_type = CODA_SUPER_MAGIC; | ||
300 | buf->f_bsize = 1024; | ||
301 | buf->f_namelen = CODA_MAXNAMLEN; | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | /* init_coda: used by filesystems.c to register coda */ | ||
307 | |||
308 | static struct super_block *coda_get_sb(struct file_system_type *fs_type, | ||
309 | int flags, const char *dev_name, void *data) | ||
310 | { | ||
311 | return get_sb_nodev(fs_type, flags, data, coda_fill_super); | ||
312 | } | ||
313 | |||
314 | struct file_system_type coda_fs_type = { | ||
315 | .owner = THIS_MODULE, | ||
316 | .name = "coda", | ||
317 | .get_sb = coda_get_sb, | ||
318 | .kill_sb = kill_anon_super, | ||
319 | .fs_flags = FS_BINARY_MOUNTDATA, | ||
320 | }; | ||
321 | |||
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c new file mode 100644 index 000000000000..127714936c66 --- /dev/null +++ b/fs/coda/pioctl.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Pioctl operations for Coda. | ||
3 | * Original version: (C) 1996 Peter Braam | ||
4 | * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University | ||
5 | * | ||
6 | * Carnegie Mellon encourages users of this code to contribute improvements | ||
7 | * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>. | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/time.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/stat.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/namei.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | |||
21 | #include <linux/coda.h> | ||
22 | #include <linux/coda_linux.h> | ||
23 | #include <linux/coda_fs_i.h> | ||
24 | #include <linux/coda_psdev.h> | ||
25 | |||
26 | /* pioctl ops */ | ||
27 | static int coda_ioctl_permission(struct inode *inode, int mask, | ||
28 | struct nameidata *nd); | ||
29 | static int coda_pioctl(struct inode * inode, struct file * filp, | ||
30 | unsigned int cmd, unsigned long user_data); | ||
31 | |||
32 | /* exported from this file */ | ||
33 | struct inode_operations coda_ioctl_inode_operations = | ||
34 | { | ||
35 | .permission = coda_ioctl_permission, | ||
36 | .setattr = coda_setattr, | ||
37 | }; | ||
38 | |||
39 | struct file_operations coda_ioctl_operations = { | ||
40 | .owner = THIS_MODULE, | ||
41 | .ioctl = coda_pioctl, | ||
42 | }; | ||
43 | |||
44 | /* the coda pioctl inode ops */ | ||
45 | static int coda_ioctl_permission(struct inode *inode, int mask, | ||
46 | struct nameidata *nd) | ||
47 | { | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int coda_pioctl(struct inode * inode, struct file * filp, | ||
52 | unsigned int cmd, unsigned long user_data) | ||
53 | { | ||
54 | struct nameidata nd; | ||
55 | int error; | ||
56 | struct PioctlData data; | ||
57 | struct inode *target_inode = NULL; | ||
58 | struct coda_inode_info *cnp; | ||
59 | |||
60 | /* get the Pioctl data arguments from user space */ | ||
61 | if (copy_from_user(&data, (void __user *)user_data, sizeof(data))) { | ||
62 | return -EINVAL; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * Look up the pathname. Note that the pathname is in | ||
67 | * user memory, and namei takes care of this | ||
68 | */ | ||
69 | if ( data.follow ) { | ||
70 | error = user_path_walk(data.path, &nd); | ||
71 | } else { | ||
72 | error = user_path_walk_link(data.path, &nd); | ||
73 | } | ||
74 | |||
75 | if ( error ) { | ||
76 | return error; | ||
77 | } else { | ||
78 | target_inode = nd.dentry->d_inode; | ||
79 | } | ||
80 | |||
81 | /* return if it is not a Coda inode */ | ||
82 | if ( target_inode->i_sb != inode->i_sb ) { | ||
83 | path_release(&nd); | ||
84 | return -EINVAL; | ||
85 | } | ||
86 | |||
87 | /* now proceed to make the upcall */ | ||
88 | cnp = ITOC(target_inode); | ||
89 | |||
90 | error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data); | ||
91 | |||
92 | path_release(&nd); | ||
93 | return error; | ||
94 | } | ||
95 | |||
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c new file mode 100644 index 000000000000..ef001a9313e6 --- /dev/null +++ b/fs/coda/psdev.c | |||
@@ -0,0 +1,462 @@ | |||
1 | /* | ||
2 | * An implementation of a loadable kernel mode driver providing | ||
3 | * multiple kernel/user space bidirectional communications links. | ||
4 | * | ||
5 | * Author: Alan Cox <alan@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * Adapted to become the Linux 2.0 Coda pseudo device | ||
13 | * Peter Braam <braam@maths.ox.ac.uk> | ||
14 | * Michael Callahan <mjc@emmy.smith.edu> | ||
15 | * | ||
16 | * Changes for Linux 2.1 | ||
17 | * Copyright (c) 1997 Carnegie-Mellon University | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/major.h> | ||
24 | #include <linux/time.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/ioport.h> | ||
27 | #include <linux/fcntl.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/skbuff.h> | ||
30 | #include <linux/proc_fs.h> | ||
31 | #include <linux/devfs_fs_kernel.h> | ||
32 | #include <linux/vmalloc.h> | ||
33 | #include <linux/fs.h> | ||
34 | #include <linux/file.h> | ||
35 | #include <linux/poll.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/list.h> | ||
38 | #include <linux/smp_lock.h> | ||
39 | #include <linux/device.h> | ||
40 | #include <asm/io.h> | ||
41 | #include <asm/system.h> | ||
42 | #include <asm/poll.h> | ||
43 | #include <asm/uaccess.h> | ||
44 | |||
45 | #include <linux/coda.h> | ||
46 | #include <linux/coda_linux.h> | ||
47 | #include <linux/coda_fs_i.h> | ||
48 | #include <linux/coda_psdev.h> | ||
49 | #include <linux/coda_proc.h> | ||
50 | |||
51 | #define upc_free(r) kfree(r) | ||
52 | |||
53 | /* | ||
54 | * Coda stuff | ||
55 | */ | ||
56 | extern struct file_system_type coda_fs_type; | ||
57 | |||
58 | /* statistics */ | ||
59 | int coda_hard; /* allows signals during upcalls */ | ||
60 | unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */ | ||
61 | |||
62 | |||
63 | struct venus_comm coda_comms[MAX_CODADEVS]; | ||
64 | static struct class_simple *coda_psdev_class; | ||
65 | |||
66 | /* | ||
67 | * Device operations | ||
68 | */ | ||
69 | |||
70 | static unsigned int coda_psdev_poll(struct file *file, poll_table * wait) | ||
71 | { | ||
72 | struct venus_comm *vcp = (struct venus_comm *) file->private_data; | ||
73 | unsigned int mask = POLLOUT | POLLWRNORM; | ||
74 | |||
75 | poll_wait(file, &vcp->vc_waitq, wait); | ||
76 | if (!list_empty(&vcp->vc_pending)) | ||
77 | mask |= POLLIN | POLLRDNORM; | ||
78 | |||
79 | return mask; | ||
80 | } | ||
81 | |||
82 | static int coda_psdev_ioctl(struct inode * inode, struct file * filp, | ||
83 | unsigned int cmd, unsigned long arg) | ||
84 | { | ||
85 | unsigned int data; | ||
86 | |||
87 | switch(cmd) { | ||
88 | case CIOC_KERNEL_VERSION: | ||
89 | data = CODA_KERNEL_VERSION; | ||
90 | return put_user(data, (int __user *) arg); | ||
91 | default: | ||
92 | return -ENOTTY; | ||
93 | } | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Receive a message written by Venus to the psdev | ||
100 | */ | ||
101 | |||
102 | static ssize_t coda_psdev_write(struct file *file, const char __user *buf, | ||
103 | size_t nbytes, loff_t *off) | ||
104 | { | ||
105 | struct venus_comm *vcp = (struct venus_comm *) file->private_data; | ||
106 | struct upc_req *req = NULL; | ||
107 | struct upc_req *tmp; | ||
108 | struct list_head *lh; | ||
109 | struct coda_in_hdr hdr; | ||
110 | ssize_t retval = 0, count = 0; | ||
111 | int error; | ||
112 | |||
113 | /* Peek at the opcode, uniquefier */ | ||
114 | if (copy_from_user(&hdr, buf, 2 * sizeof(u_long))) | ||
115 | return -EFAULT; | ||
116 | |||
117 | if (DOWNCALL(hdr.opcode)) { | ||
118 | struct super_block *sb = NULL; | ||
119 | union outputArgs *dcbuf; | ||
120 | int size = sizeof(*dcbuf); | ||
121 | |||
122 | sb = vcp->vc_sb; | ||
123 | if ( !sb ) { | ||
124 | count = nbytes; | ||
125 | goto out; | ||
126 | } | ||
127 | |||
128 | if ( nbytes < sizeof(struct coda_out_hdr) ) { | ||
129 | printk("coda_downcall opc %d uniq %d, not enough!\n", | ||
130 | hdr.opcode, hdr.unique); | ||
131 | count = nbytes; | ||
132 | goto out; | ||
133 | } | ||
134 | if ( nbytes > size ) { | ||
135 | printk("Coda: downcall opc %d, uniq %d, too much!", | ||
136 | hdr.opcode, hdr.unique); | ||
137 | nbytes = size; | ||
138 | } | ||
139 | CODA_ALLOC(dcbuf, union outputArgs *, nbytes); | ||
140 | if (copy_from_user(dcbuf, buf, nbytes)) { | ||
141 | CODA_FREE(dcbuf, nbytes); | ||
142 | retval = -EFAULT; | ||
143 | goto out; | ||
144 | } | ||
145 | |||
146 | /* what downcall errors does Venus handle ? */ | ||
147 | lock_kernel(); | ||
148 | error = coda_downcall(hdr.opcode, dcbuf, sb); | ||
149 | unlock_kernel(); | ||
150 | |||
151 | CODA_FREE(dcbuf, nbytes); | ||
152 | if (error) { | ||
153 | printk("psdev_write: coda_downcall error: %d\n", error); | ||
154 | retval = error; | ||
155 | goto out; | ||
156 | } | ||
157 | count = nbytes; | ||
158 | goto out; | ||
159 | } | ||
160 | |||
161 | /* Look for the message on the processing queue. */ | ||
162 | lock_kernel(); | ||
163 | list_for_each(lh, &vcp->vc_processing) { | ||
164 | tmp = list_entry(lh, struct upc_req , uc_chain); | ||
165 | if (tmp->uc_unique == hdr.unique) { | ||
166 | req = tmp; | ||
167 | list_del(&req->uc_chain); | ||
168 | break; | ||
169 | } | ||
170 | } | ||
171 | unlock_kernel(); | ||
172 | |||
173 | if (!req) { | ||
174 | printk("psdev_write: msg (%d, %d) not found\n", | ||
175 | hdr.opcode, hdr.unique); | ||
176 | retval = -ESRCH; | ||
177 | goto out; | ||
178 | } | ||
179 | |||
180 | /* move data into response buffer. */ | ||
181 | if (req->uc_outSize < nbytes) { | ||
182 | printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n", | ||
183 | req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique); | ||
184 | nbytes = req->uc_outSize; /* don't have more space! */ | ||
185 | } | ||
186 | if (copy_from_user(req->uc_data, buf, nbytes)) { | ||
187 | req->uc_flags |= REQ_ABORT; | ||
188 | wake_up(&req->uc_sleep); | ||
189 | retval = -EFAULT; | ||
190 | goto out; | ||
191 | } | ||
192 | |||
193 | /* adjust outsize. is this useful ?? */ | ||
194 | req->uc_outSize = nbytes; | ||
195 | req->uc_flags |= REQ_WRITE; | ||
196 | count = nbytes; | ||
197 | |||
198 | /* Convert filedescriptor into a file handle */ | ||
199 | if (req->uc_opcode == CODA_OPEN_BY_FD) { | ||
200 | struct coda_open_by_fd_out *outp = | ||
201 | (struct coda_open_by_fd_out *)req->uc_data; | ||
202 | outp->fh = fget(outp->fd); | ||
203 | } | ||
204 | |||
205 | wake_up(&req->uc_sleep); | ||
206 | out: | ||
207 | return(count ? count : retval); | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Read a message from the kernel to Venus | ||
212 | */ | ||
213 | |||
214 | static ssize_t coda_psdev_read(struct file * file, char __user * buf, | ||
215 | size_t nbytes, loff_t *off) | ||
216 | { | ||
217 | DECLARE_WAITQUEUE(wait, current); | ||
218 | struct venus_comm *vcp = (struct venus_comm *) file->private_data; | ||
219 | struct upc_req *req; | ||
220 | ssize_t retval = 0, count = 0; | ||
221 | |||
222 | if (nbytes == 0) | ||
223 | return 0; | ||
224 | |||
225 | lock_kernel(); | ||
226 | |||
227 | add_wait_queue(&vcp->vc_waitq, &wait); | ||
228 | set_current_state(TASK_INTERRUPTIBLE); | ||
229 | |||
230 | while (list_empty(&vcp->vc_pending)) { | ||
231 | if (file->f_flags & O_NONBLOCK) { | ||
232 | retval = -EAGAIN; | ||
233 | break; | ||
234 | } | ||
235 | if (signal_pending(current)) { | ||
236 | retval = -ERESTARTSYS; | ||
237 | break; | ||
238 | } | ||
239 | schedule(); | ||
240 | } | ||
241 | |||
242 | set_current_state(TASK_RUNNING); | ||
243 | remove_wait_queue(&vcp->vc_waitq, &wait); | ||
244 | |||
245 | if (retval) | ||
246 | goto out; | ||
247 | |||
248 | req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain); | ||
249 | list_del(&req->uc_chain); | ||
250 | |||
251 | /* Move the input args into userspace */ | ||
252 | count = req->uc_inSize; | ||
253 | if (nbytes < req->uc_inSize) { | ||
254 | printk ("psdev_read: Venus read %ld bytes of %d in message\n", | ||
255 | (long)nbytes, req->uc_inSize); | ||
256 | count = nbytes; | ||
257 | } | ||
258 | |||
259 | if (copy_to_user(buf, req->uc_data, count)) | ||
260 | retval = -EFAULT; | ||
261 | |||
262 | /* If request was not a signal, enqueue and don't free */ | ||
263 | if (!(req->uc_flags & REQ_ASYNC)) { | ||
264 | req->uc_flags |= REQ_READ; | ||
265 | list_add(&(req->uc_chain), vcp->vc_processing.prev); | ||
266 | goto out; | ||
267 | } | ||
268 | |||
269 | CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); | ||
270 | upc_free(req); | ||
271 | out: | ||
272 | unlock_kernel(); | ||
273 | return (count ? count : retval); | ||
274 | } | ||
275 | |||
276 | static int coda_psdev_open(struct inode * inode, struct file * file) | ||
277 | { | ||
278 | struct venus_comm *vcp; | ||
279 | int idx; | ||
280 | |||
281 | lock_kernel(); | ||
282 | idx = iminor(inode); | ||
283 | if(idx >= MAX_CODADEVS) { | ||
284 | unlock_kernel(); | ||
285 | return -ENODEV; | ||
286 | } | ||
287 | |||
288 | vcp = &coda_comms[idx]; | ||
289 | if(vcp->vc_inuse) { | ||
290 | unlock_kernel(); | ||
291 | return -EBUSY; | ||
292 | } | ||
293 | |||
294 | if (!vcp->vc_inuse++) { | ||
295 | INIT_LIST_HEAD(&vcp->vc_pending); | ||
296 | INIT_LIST_HEAD(&vcp->vc_processing); | ||
297 | init_waitqueue_head(&vcp->vc_waitq); | ||
298 | vcp->vc_sb = NULL; | ||
299 | vcp->vc_seq = 0; | ||
300 | } | ||
301 | |||
302 | file->private_data = vcp; | ||
303 | |||
304 | unlock_kernel(); | ||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | |||
309 | static int coda_psdev_release(struct inode * inode, struct file * file) | ||
310 | { | ||
311 | struct venus_comm *vcp = (struct venus_comm *) file->private_data; | ||
312 | struct upc_req *req, *tmp; | ||
313 | |||
314 | lock_kernel(); | ||
315 | if ( !vcp->vc_inuse ) { | ||
316 | unlock_kernel(); | ||
317 | printk("psdev_release: Not open.\n"); | ||
318 | return -1; | ||
319 | } | ||
320 | |||
321 | if (--vcp->vc_inuse) { | ||
322 | unlock_kernel(); | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | /* Wakeup clients so they can return. */ | ||
327 | list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) { | ||
328 | /* Async requests need to be freed here */ | ||
329 | if (req->uc_flags & REQ_ASYNC) { | ||
330 | CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); | ||
331 | upc_free(req); | ||
332 | continue; | ||
333 | } | ||
334 | req->uc_flags |= REQ_ABORT; | ||
335 | wake_up(&req->uc_sleep); | ||
336 | } | ||
337 | |||
338 | list_for_each_entry(req, &vcp->vc_processing, uc_chain) { | ||
339 | req->uc_flags |= REQ_ABORT; | ||
340 | wake_up(&req->uc_sleep); | ||
341 | } | ||
342 | |||
343 | unlock_kernel(); | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | |||
348 | static struct file_operations coda_psdev_fops = { | ||
349 | .owner = THIS_MODULE, | ||
350 | .read = coda_psdev_read, | ||
351 | .write = coda_psdev_write, | ||
352 | .poll = coda_psdev_poll, | ||
353 | .ioctl = coda_psdev_ioctl, | ||
354 | .open = coda_psdev_open, | ||
355 | .release = coda_psdev_release, | ||
356 | }; | ||
357 | |||
358 | static int init_coda_psdev(void) | ||
359 | { | ||
360 | int i, err = 0; | ||
361 | if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) { | ||
362 | printk(KERN_ERR "coda_psdev: unable to get major %d\n", | ||
363 | CODA_PSDEV_MAJOR); | ||
364 | return -EIO; | ||
365 | } | ||
366 | coda_psdev_class = class_simple_create(THIS_MODULE, "coda"); | ||
367 | if (IS_ERR(coda_psdev_class)) { | ||
368 | err = PTR_ERR(coda_psdev_class); | ||
369 | goto out_chrdev; | ||
370 | } | ||
371 | devfs_mk_dir ("coda"); | ||
372 | for (i = 0; i < MAX_CODADEVS; i++) { | ||
373 | class_simple_device_add(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR,i), | ||
374 | NULL, "cfs%d", i); | ||
375 | err = devfs_mk_cdev(MKDEV(CODA_PSDEV_MAJOR, i), | ||
376 | S_IFCHR|S_IRUSR|S_IWUSR, "coda/%d", i); | ||
377 | if (err) | ||
378 | goto out_class; | ||
379 | } | ||
380 | coda_sysctl_init(); | ||
381 | goto out; | ||
382 | |||
383 | out_class: | ||
384 | for (i = 0; i < MAX_CODADEVS; i++) | ||
385 | class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i)); | ||
386 | class_simple_destroy(coda_psdev_class); | ||
387 | out_chrdev: | ||
388 | unregister_chrdev(CODA_PSDEV_MAJOR, "coda"); | ||
389 | out: | ||
390 | return err; | ||
391 | } | ||
392 | |||
393 | |||
394 | MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>"); | ||
395 | MODULE_LICENSE("GPL"); | ||
396 | |||
397 | extern int coda_init_inodecache(void); | ||
398 | extern void coda_destroy_inodecache(void); | ||
399 | static int __init init_coda(void) | ||
400 | { | ||
401 | int status; | ||
402 | int i; | ||
403 | printk(KERN_INFO "Coda Kernel/Venus communications, " | ||
404 | #ifdef CONFIG_CODA_FS_OLD_API | ||
405 | "v5.3.20" | ||
406 | #else | ||
407 | "v6.0.0" | ||
408 | #endif | ||
409 | ", coda@cs.cmu.edu\n"); | ||
410 | |||
411 | status = coda_init_inodecache(); | ||
412 | if (status) | ||
413 | goto out2; | ||
414 | status = init_coda_psdev(); | ||
415 | if ( status ) { | ||
416 | printk("Problem (%d) in init_coda_psdev\n", status); | ||
417 | goto out1; | ||
418 | } | ||
419 | |||
420 | status = register_filesystem(&coda_fs_type); | ||
421 | if (status) { | ||
422 | printk("coda: failed to register filesystem!\n"); | ||
423 | goto out; | ||
424 | } | ||
425 | return 0; | ||
426 | out: | ||
427 | for (i = 0; i < MAX_CODADEVS; i++) { | ||
428 | class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i)); | ||
429 | devfs_remove("coda/%d", i); | ||
430 | } | ||
431 | class_simple_destroy(coda_psdev_class); | ||
432 | devfs_remove("coda"); | ||
433 | unregister_chrdev(CODA_PSDEV_MAJOR, "coda"); | ||
434 | coda_sysctl_clean(); | ||
435 | out1: | ||
436 | coda_destroy_inodecache(); | ||
437 | out2: | ||
438 | return status; | ||
439 | } | ||
440 | |||
441 | static void __exit exit_coda(void) | ||
442 | { | ||
443 | int err, i; | ||
444 | |||
445 | err = unregister_filesystem(&coda_fs_type); | ||
446 | if ( err != 0 ) { | ||
447 | printk("coda: failed to unregister filesystem\n"); | ||
448 | } | ||
449 | for (i = 0; i < MAX_CODADEVS; i++) { | ||
450 | class_simple_device_remove(MKDEV(CODA_PSDEV_MAJOR, i)); | ||
451 | devfs_remove("coda/%d", i); | ||
452 | } | ||
453 | class_simple_destroy(coda_psdev_class); | ||
454 | devfs_remove("coda"); | ||
455 | unregister_chrdev(CODA_PSDEV_MAJOR, "coda"); | ||
456 | coda_sysctl_clean(); | ||
457 | coda_destroy_inodecache(); | ||
458 | } | ||
459 | |||
460 | module_init(init_coda); | ||
461 | module_exit(exit_coda); | ||
462 | |||
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c new file mode 100644 index 000000000000..b35e5bbd9c99 --- /dev/null +++ b/fs/coda/symlink.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * Symlink inode operations for Coda filesystem | ||
3 | * Original version: (C) 1996 P. Braam and M. Callahan | ||
4 | * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University | ||
5 | * | ||
6 | * Carnegie Mellon encourages users to contribute improvements to | ||
7 | * the Coda project. Contact Peter Braam (coda@cs.cmu.edu). | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/time.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/stat.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/pagemap.h> | ||
17 | #include <linux/smp_lock.h> | ||
18 | |||
19 | #include <linux/coda.h> | ||
20 | #include <linux/coda_linux.h> | ||
21 | #include <linux/coda_psdev.h> | ||
22 | #include <linux/coda_fs_i.h> | ||
23 | #include <linux/coda_proc.h> | ||
24 | |||
25 | static int coda_symlink_filler(struct file *file, struct page *page) | ||
26 | { | ||
27 | struct inode *inode = page->mapping->host; | ||
28 | int error; | ||
29 | struct coda_inode_info *cii; | ||
30 | unsigned int len = PAGE_SIZE; | ||
31 | char *p = kmap(page); | ||
32 | |||
33 | lock_kernel(); | ||
34 | cii = ITOC(inode); | ||
35 | coda_vfs_stat.follow_link++; | ||
36 | |||
37 | error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len); | ||
38 | unlock_kernel(); | ||
39 | if (error) | ||
40 | goto fail; | ||
41 | SetPageUptodate(page); | ||
42 | kunmap(page); | ||
43 | unlock_page(page); | ||
44 | return 0; | ||
45 | |||
46 | fail: | ||
47 | SetPageError(page); | ||
48 | kunmap(page); | ||
49 | unlock_page(page); | ||
50 | return error; | ||
51 | } | ||
52 | |||
53 | struct address_space_operations coda_symlink_aops = { | ||
54 | .readpage = coda_symlink_filler, | ||
55 | }; | ||
diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c new file mode 100644 index 000000000000..f0b10757288f --- /dev/null +++ b/fs/coda/sysctl.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* | ||
2 | * Sysctl operations for Coda filesystem | ||
3 | * Original version: (C) 1996 P. Braam and M. Callahan | ||
4 | * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University | ||
5 | * | ||
6 | * Carnegie Mellon encourages users to contribute improvements to | ||
7 | * the Coda project. Contact Peter Braam (coda@cs.cmu.edu). | ||
8 | * | ||
9 | * CODA operation statistics | ||
10 | * (c) March, 1998 Zhanyong Wan <zhanyong.wan@yale.edu> | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/time.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/sysctl.h> | ||
18 | #include <linux/proc_fs.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/stat.h> | ||
21 | #include <linux/ctype.h> | ||
22 | #include <linux/bitops.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | #include <linux/utsname.h> | ||
25 | #include <linux/module.h> | ||
26 | |||
27 | #include <linux/coda.h> | ||
28 | #include <linux/coda_linux.h> | ||
29 | #include <linux/coda_fs_i.h> | ||
30 | #include <linux/coda_psdev.h> | ||
31 | #include <linux/coda_cache.h> | ||
32 | #include <linux/coda_proc.h> | ||
33 | |||
34 | static struct ctl_table_header *fs_table_header; | ||
35 | |||
36 | #define FS_CODA 1 /* Coda file system */ | ||
37 | |||
38 | #define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */ | ||
39 | #define CODA_HARD 5 /* mount type "hard" or "soft" */ | ||
40 | #define CODA_VFS 6 /* vfs statistics */ | ||
41 | #define CODA_CACHE_INV 9 /* cache invalidation statistics */ | ||
42 | #define CODA_FAKE_STATFS 10 /* don't query venus for actual cache usage */ | ||
43 | |||
44 | struct coda_vfs_stats coda_vfs_stat; | ||
45 | static struct coda_cache_inv_stats coda_cache_inv_stat; | ||
46 | |||
47 | static void reset_coda_vfs_stats( void ) | ||
48 | { | ||
49 | memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) ); | ||
50 | } | ||
51 | |||
52 | static void reset_coda_cache_inv_stats( void ) | ||
53 | { | ||
54 | memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) ); | ||
55 | } | ||
56 | |||
57 | static int do_reset_coda_vfs_stats( ctl_table * table, int write, | ||
58 | struct file * filp, void __user * buffer, | ||
59 | size_t * lenp, loff_t * ppos ) | ||
60 | { | ||
61 | if ( write ) { | ||
62 | reset_coda_vfs_stats(); | ||
63 | |||
64 | *ppos += *lenp; | ||
65 | } else { | ||
66 | *lenp = 0; | ||
67 | } | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int do_reset_coda_cache_inv_stats( ctl_table * table, int write, | ||
73 | struct file * filp, | ||
74 | void __user * buffer, | ||
75 | size_t * lenp, loff_t * ppos ) | ||
76 | { | ||
77 | if ( write ) { | ||
78 | reset_coda_cache_inv_stats(); | ||
79 | |||
80 | *ppos += *lenp; | ||
81 | } else { | ||
82 | *lenp = 0; | ||
83 | } | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int coda_vfs_stats_get_info( char * buffer, char ** start, | ||
89 | off_t offset, int length) | ||
90 | { | ||
91 | int len=0; | ||
92 | off_t begin; | ||
93 | struct coda_vfs_stats * ps = & coda_vfs_stat; | ||
94 | |||
95 | /* this works as long as we are below 1024 characters! */ | ||
96 | len += sprintf( buffer, | ||
97 | "Coda VFS statistics\n" | ||
98 | "===================\n\n" | ||
99 | "File Operations:\n" | ||
100 | "\topen\t\t%9d\n" | ||
101 | "\tflush\t\t%9d\n" | ||
102 | "\trelease\t\t%9d\n" | ||
103 | "\tfsync\t\t%9d\n\n" | ||
104 | "Dir Operations:\n" | ||
105 | "\treaddir\t\t%9d\n\n" | ||
106 | "Inode Operations\n" | ||
107 | "\tcreate\t\t%9d\n" | ||
108 | "\tlookup\t\t%9d\n" | ||
109 | "\tlink\t\t%9d\n" | ||
110 | "\tunlink\t\t%9d\n" | ||
111 | "\tsymlink\t\t%9d\n" | ||
112 | "\tmkdir\t\t%9d\n" | ||
113 | "\trmdir\t\t%9d\n" | ||
114 | "\trename\t\t%9d\n" | ||
115 | "\tpermission\t%9d\n", | ||
116 | |||
117 | /* file operations */ | ||
118 | ps->open, | ||
119 | ps->flush, | ||
120 | ps->release, | ||
121 | ps->fsync, | ||
122 | |||
123 | /* dir operations */ | ||
124 | ps->readdir, | ||
125 | |||
126 | /* inode operations */ | ||
127 | ps->create, | ||
128 | ps->lookup, | ||
129 | ps->link, | ||
130 | ps->unlink, | ||
131 | ps->symlink, | ||
132 | ps->mkdir, | ||
133 | ps->rmdir, | ||
134 | ps->rename, | ||
135 | ps->permission); | ||
136 | |||
137 | begin = offset; | ||
138 | *start = buffer + begin; | ||
139 | len -= begin; | ||
140 | |||
141 | if ( len > length ) | ||
142 | len = length; | ||
143 | if ( len < 0 ) | ||
144 | len = 0; | ||
145 | |||
146 | return len; | ||
147 | } | ||
148 | |||
149 | static int coda_cache_inv_stats_get_info( char * buffer, char ** start, | ||
150 | off_t offset, int length) | ||
151 | { | ||
152 | int len=0; | ||
153 | off_t begin; | ||
154 | struct coda_cache_inv_stats * ps = & coda_cache_inv_stat; | ||
155 | |||
156 | /* this works as long as we are below 1024 characters! */ | ||
157 | len += sprintf( buffer, | ||
158 | "Coda cache invalidation statistics\n" | ||
159 | "==================================\n\n" | ||
160 | "flush\t\t%9d\n" | ||
161 | "purge user\t%9d\n" | ||
162 | "zap_dir\t\t%9d\n" | ||
163 | "zap_file\t%9d\n" | ||
164 | "zap_vnode\t%9d\n" | ||
165 | "purge_fid\t%9d\n" | ||
166 | "replace\t\t%9d\n", | ||
167 | ps->flush, | ||
168 | ps->purge_user, | ||
169 | ps->zap_dir, | ||
170 | ps->zap_file, | ||
171 | ps->zap_vnode, | ||
172 | ps->purge_fid, | ||
173 | ps->replace ); | ||
174 | |||
175 | begin = offset; | ||
176 | *start = buffer + begin; | ||
177 | len -= begin; | ||
178 | |||
179 | if ( len > length ) | ||
180 | len = length; | ||
181 | if ( len < 0 ) | ||
182 | len = 0; | ||
183 | |||
184 | return len; | ||
185 | } | ||
186 | |||
187 | static ctl_table coda_table[] = { | ||
188 | {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, | ||
189 | {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &proc_dointvec}, | ||
190 | {CODA_VFS, "vfs_stats", NULL, 0, 0644, NULL, &do_reset_coda_vfs_stats}, | ||
191 | {CODA_CACHE_INV, "cache_inv_stats", NULL, 0, 0644, NULL, &do_reset_coda_cache_inv_stats}, | ||
192 | {CODA_FAKE_STATFS, "fake_statfs", &coda_fake_statfs, sizeof(int), 0600, NULL, &proc_dointvec}, | ||
193 | { 0 } | ||
194 | }; | ||
195 | |||
196 | static ctl_table fs_table[] = { | ||
197 | {FS_CODA, "coda", NULL, 0, 0555, coda_table}, | ||
198 | {0} | ||
199 | }; | ||
200 | |||
201 | |||
202 | #ifdef CONFIG_PROC_FS | ||
203 | |||
204 | /* | ||
205 | target directory structure: | ||
206 | /proc/fs (see linux/fs/proc/root.c) | ||
207 | /proc/fs/coda | ||
208 | /proc/fs/coda/{vfs_stats, | ||
209 | |||
210 | */ | ||
211 | |||
212 | static struct proc_dir_entry* proc_fs_coda; | ||
213 | |||
214 | #endif | ||
215 | |||
216 | #define coda_proc_create(name,get_info) \ | ||
217 | create_proc_info_entry(name, 0, proc_fs_coda, get_info) | ||
218 | |||
219 | void coda_sysctl_init(void) | ||
220 | { | ||
221 | reset_coda_vfs_stats(); | ||
222 | reset_coda_cache_inv_stats(); | ||
223 | |||
224 | #ifdef CONFIG_PROC_FS | ||
225 | proc_fs_coda = proc_mkdir("coda", proc_root_fs); | ||
226 | if (proc_fs_coda) { | ||
227 | proc_fs_coda->owner = THIS_MODULE; | ||
228 | coda_proc_create("vfs_stats", coda_vfs_stats_get_info); | ||
229 | coda_proc_create("cache_inv_stats", coda_cache_inv_stats_get_info); | ||
230 | } | ||
231 | #endif | ||
232 | |||
233 | #ifdef CONFIG_SYSCTL | ||
234 | if ( !fs_table_header ) | ||
235 | fs_table_header = register_sysctl_table(fs_table, 0); | ||
236 | #endif | ||
237 | } | ||
238 | |||
239 | void coda_sysctl_clean(void) | ||
240 | { | ||
241 | |||
242 | #ifdef CONFIG_SYSCTL | ||
243 | if ( fs_table_header ) { | ||
244 | unregister_sysctl_table(fs_table_header); | ||
245 | fs_table_header = NULL; | ||
246 | } | ||
247 | #endif | ||
248 | |||
249 | #ifdef CONFIG_PROC_FS | ||
250 | remove_proc_entry("cache_inv_stats", proc_fs_coda); | ||
251 | remove_proc_entry("vfs_stats", proc_fs_coda); | ||
252 | remove_proc_entry("coda", proc_root_fs); | ||
253 | #endif | ||
254 | } | ||
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c new file mode 100644 index 000000000000..1bae99650a91 --- /dev/null +++ b/fs/coda/upcall.c | |||
@@ -0,0 +1,914 @@ | |||
1 | /* | ||
2 | * Mostly platform independent upcall operations to Venus: | ||
3 | * -- upcalls | ||
4 | * -- upcall routines | ||
5 | * | ||
6 | * Linux 2.0 version | ||
7 | * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, | ||
8 | * Michael Callahan <callahan@maths.ox.ac.uk> | ||
9 | * | ||
10 | * Redone for Linux 2.1 | ||
11 | * Copyright (C) 1997 Carnegie Mellon University | ||
12 | * | ||
13 | * Carnegie Mellon University encourages users of this code to contribute | ||
14 | * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>. | ||
15 | */ | ||
16 | |||
17 | #include <asm/system.h> | ||
18 | #include <linux/signal.h> | ||
19 | |||
20 | #include <linux/types.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/time.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/file.h> | ||
26 | #include <linux/stat.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/string.h> | ||
29 | #include <asm/uaccess.h> | ||
30 | #include <linux/vmalloc.h> | ||
31 | #include <linux/vfs.h> | ||
32 | |||
33 | #include <linux/coda.h> | ||
34 | #include <linux/coda_linux.h> | ||
35 | #include <linux/coda_psdev.h> | ||
36 | #include <linux/coda_fs_i.h> | ||
37 | #include <linux/coda_cache.h> | ||
38 | #include <linux/coda_proc.h> | ||
39 | |||
40 | #define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL) | ||
41 | #define upc_free(r) kfree(r) | ||
42 | |||
43 | static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize, | ||
44 | union inputArgs *buffer); | ||
45 | |||
46 | static void *alloc_upcall(int opcode, int size) | ||
47 | { | ||
48 | union inputArgs *inp; | ||
49 | |||
50 | CODA_ALLOC(inp, union inputArgs *, size); | ||
51 | if (!inp) | ||
52 | return ERR_PTR(-ENOMEM); | ||
53 | |||
54 | inp->ih.opcode = opcode; | ||
55 | inp->ih.pid = current->pid; | ||
56 | inp->ih.pgid = process_group(current); | ||
57 | #ifdef CONFIG_CODA_FS_OLD_API | ||
58 | memset(&inp->ih.cred, 0, sizeof(struct coda_cred)); | ||
59 | inp->ih.cred.cr_fsuid = current->fsuid; | ||
60 | #else | ||
61 | inp->ih.uid = current->fsuid; | ||
62 | #endif | ||
63 | return (void*)inp; | ||
64 | } | ||
65 | |||
66 | #define UPARG(op)\ | ||
67 | do {\ | ||
68 | inp = (union inputArgs *)alloc_upcall(op, insize); \ | ||
69 | if (IS_ERR(inp)) { return PTR_ERR(inp); }\ | ||
70 | outp = (union outputArgs *)(inp); \ | ||
71 | outsize = insize; \ | ||
72 | } while (0) | ||
73 | |||
74 | #define INSIZE(tag) sizeof(struct coda_ ## tag ## _in) | ||
75 | #define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out) | ||
76 | #define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag)) | ||
77 | |||
78 | |||
79 | /* the upcalls */ | ||
80 | int venus_rootfid(struct super_block *sb, struct CodaFid *fidp) | ||
81 | { | ||
82 | union inputArgs *inp; | ||
83 | union outputArgs *outp; | ||
84 | int insize, outsize, error; | ||
85 | |||
86 | insize = SIZE(root); | ||
87 | UPARG(CODA_ROOT); | ||
88 | |||
89 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
90 | |||
91 | if (error) { | ||
92 | printk("coda_get_rootfid: error %d\n", error); | ||
93 | } else { | ||
94 | *fidp = outp->coda_root.VFid; | ||
95 | } | ||
96 | |||
97 | CODA_FREE(inp, insize); | ||
98 | return error; | ||
99 | } | ||
100 | |||
101 | int venus_getattr(struct super_block *sb, struct CodaFid *fid, | ||
102 | struct coda_vattr *attr) | ||
103 | { | ||
104 | union inputArgs *inp; | ||
105 | union outputArgs *outp; | ||
106 | int insize, outsize, error; | ||
107 | |||
108 | insize = SIZE(getattr); | ||
109 | UPARG(CODA_GETATTR); | ||
110 | inp->coda_getattr.VFid = *fid; | ||
111 | |||
112 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
113 | |||
114 | *attr = outp->coda_getattr.attr; | ||
115 | |||
116 | CODA_FREE(inp, insize); | ||
117 | return error; | ||
118 | } | ||
119 | |||
120 | int venus_setattr(struct super_block *sb, struct CodaFid *fid, | ||
121 | struct coda_vattr *vattr) | ||
122 | { | ||
123 | union inputArgs *inp; | ||
124 | union outputArgs *outp; | ||
125 | int insize, outsize, error; | ||
126 | |||
127 | insize = SIZE(setattr); | ||
128 | UPARG(CODA_SETATTR); | ||
129 | |||
130 | inp->coda_setattr.VFid = *fid; | ||
131 | inp->coda_setattr.attr = *vattr; | ||
132 | |||
133 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
134 | |||
135 | CODA_FREE(inp, insize); | ||
136 | return error; | ||
137 | } | ||
138 | |||
139 | int venus_lookup(struct super_block *sb, struct CodaFid *fid, | ||
140 | const char *name, int length, int * type, | ||
141 | struct CodaFid *resfid) | ||
142 | { | ||
143 | union inputArgs *inp; | ||
144 | union outputArgs *outp; | ||
145 | int insize, outsize, error; | ||
146 | int offset; | ||
147 | |||
148 | offset = INSIZE(lookup); | ||
149 | insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup)); | ||
150 | UPARG(CODA_LOOKUP); | ||
151 | |||
152 | inp->coda_lookup.VFid = *fid; | ||
153 | inp->coda_lookup.name = offset; | ||
154 | inp->coda_lookup.flags = CLU_CASE_SENSITIVE; | ||
155 | /* send Venus a null terminated string */ | ||
156 | memcpy((char *)(inp) + offset, name, length); | ||
157 | *((char *)inp + offset + length) = '\0'; | ||
158 | |||
159 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
160 | |||
161 | *resfid = outp->coda_lookup.VFid; | ||
162 | *type = outp->coda_lookup.vtype; | ||
163 | |||
164 | CODA_FREE(inp, insize); | ||
165 | return error; | ||
166 | } | ||
167 | |||
168 | int venus_store(struct super_block *sb, struct CodaFid *fid, int flags, | ||
169 | vuid_t uid) | ||
170 | { | ||
171 | union inputArgs *inp; | ||
172 | union outputArgs *outp; | ||
173 | int insize, outsize, error; | ||
174 | #ifdef CONFIG_CODA_FS_OLD_API | ||
175 | struct coda_cred cred = { 0, }; | ||
176 | cred.cr_fsuid = uid; | ||
177 | #endif | ||
178 | |||
179 | insize = SIZE(store); | ||
180 | UPARG(CODA_STORE); | ||
181 | |||
182 | #ifdef CONFIG_CODA_FS_OLD_API | ||
183 | memcpy(&(inp->ih.cred), &cred, sizeof(cred)); | ||
184 | #else | ||
185 | inp->ih.uid = uid; | ||
186 | #endif | ||
187 | |||
188 | inp->coda_store.VFid = *fid; | ||
189 | inp->coda_store.flags = flags; | ||
190 | |||
191 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
192 | |||
193 | CODA_FREE(inp, insize); | ||
194 | return error; | ||
195 | } | ||
196 | |||
197 | int venus_release(struct super_block *sb, struct CodaFid *fid, int flags) | ||
198 | { | ||
199 | union inputArgs *inp; | ||
200 | union outputArgs *outp; | ||
201 | int insize, outsize, error; | ||
202 | |||
203 | insize = SIZE(release); | ||
204 | UPARG(CODA_RELEASE); | ||
205 | |||
206 | inp->coda_release.VFid = *fid; | ||
207 | inp->coda_release.flags = flags; | ||
208 | |||
209 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
210 | |||
211 | CODA_FREE(inp, insize); | ||
212 | return error; | ||
213 | } | ||
214 | |||
215 | int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, | ||
216 | vuid_t uid) | ||
217 | { | ||
218 | union inputArgs *inp; | ||
219 | union outputArgs *outp; | ||
220 | int insize, outsize, error; | ||
221 | #ifdef CONFIG_CODA_FS_OLD_API | ||
222 | struct coda_cred cred = { 0, }; | ||
223 | cred.cr_fsuid = uid; | ||
224 | #endif | ||
225 | |||
226 | insize = SIZE(release); | ||
227 | UPARG(CODA_CLOSE); | ||
228 | |||
229 | #ifdef CONFIG_CODA_FS_OLD_API | ||
230 | memcpy(&(inp->ih.cred), &cred, sizeof(cred)); | ||
231 | #else | ||
232 | inp->ih.uid = uid; | ||
233 | #endif | ||
234 | |||
235 | inp->coda_close.VFid = *fid; | ||
236 | inp->coda_close.flags = flags; | ||
237 | |||
238 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
239 | |||
240 | CODA_FREE(inp, insize); | ||
241 | return error; | ||
242 | } | ||
243 | |||
244 | int venus_open(struct super_block *sb, struct CodaFid *fid, | ||
245 | int flags, struct file **fh) | ||
246 | { | ||
247 | union inputArgs *inp; | ||
248 | union outputArgs *outp; | ||
249 | int insize, outsize, error; | ||
250 | |||
251 | insize = SIZE(open_by_fd); | ||
252 | UPARG(CODA_OPEN_BY_FD); | ||
253 | |||
254 | inp->coda_open.VFid = *fid; | ||
255 | inp->coda_open.flags = flags; | ||
256 | |||
257 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
258 | |||
259 | *fh = outp->coda_open_by_fd.fh; | ||
260 | |||
261 | CODA_FREE(inp, insize); | ||
262 | return error; | ||
263 | } | ||
264 | |||
265 | int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, | ||
266 | const char *name, int length, | ||
267 | struct CodaFid *newfid, struct coda_vattr *attrs) | ||
268 | { | ||
269 | union inputArgs *inp; | ||
270 | union outputArgs *outp; | ||
271 | int insize, outsize, error; | ||
272 | int offset; | ||
273 | |||
274 | offset = INSIZE(mkdir); | ||
275 | insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir)); | ||
276 | UPARG(CODA_MKDIR); | ||
277 | |||
278 | inp->coda_mkdir.VFid = *dirfid; | ||
279 | inp->coda_mkdir.attr = *attrs; | ||
280 | inp->coda_mkdir.name = offset; | ||
281 | /* Venus must get null terminated string */ | ||
282 | memcpy((char *)(inp) + offset, name, length); | ||
283 | *((char *)inp + offset + length) = '\0'; | ||
284 | |||
285 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
286 | |||
287 | *attrs = outp->coda_mkdir.attr; | ||
288 | *newfid = outp->coda_mkdir.VFid; | ||
289 | |||
290 | CODA_FREE(inp, insize); | ||
291 | return error; | ||
292 | } | ||
293 | |||
294 | |||
295 | int venus_rename(struct super_block *sb, struct CodaFid *old_fid, | ||
296 | struct CodaFid *new_fid, size_t old_length, | ||
297 | size_t new_length, const char *old_name, | ||
298 | const char *new_name) | ||
299 | { | ||
300 | union inputArgs *inp; | ||
301 | union outputArgs *outp; | ||
302 | int insize, outsize, error; | ||
303 | int offset, s; | ||
304 | |||
305 | offset = INSIZE(rename); | ||
306 | insize = max_t(unsigned int, offset + new_length + old_length + 8, | ||
307 | OUTSIZE(rename)); | ||
308 | UPARG(CODA_RENAME); | ||
309 | |||
310 | inp->coda_rename.sourceFid = *old_fid; | ||
311 | inp->coda_rename.destFid = *new_fid; | ||
312 | inp->coda_rename.srcname = offset; | ||
313 | |||
314 | /* Venus must receive an null terminated string */ | ||
315 | s = ( old_length & ~0x3) +4; /* round up to word boundary */ | ||
316 | memcpy((char *)(inp) + offset, old_name, old_length); | ||
317 | *((char *)inp + offset + old_length) = '\0'; | ||
318 | |||
319 | /* another null terminated string for Venus */ | ||
320 | offset += s; | ||
321 | inp->coda_rename.destname = offset; | ||
322 | s = ( new_length & ~0x3) +4; /* round up to word boundary */ | ||
323 | memcpy((char *)(inp) + offset, new_name, new_length); | ||
324 | *((char *)inp + offset + new_length) = '\0'; | ||
325 | |||
326 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
327 | |||
328 | CODA_FREE(inp, insize); | ||
329 | return error; | ||
330 | } | ||
331 | |||
332 | int venus_create(struct super_block *sb, struct CodaFid *dirfid, | ||
333 | const char *name, int length, int excl, int mode, | ||
334 | struct CodaFid *newfid, struct coda_vattr *attrs) | ||
335 | { | ||
336 | union inputArgs *inp; | ||
337 | union outputArgs *outp; | ||
338 | int insize, outsize, error; | ||
339 | int offset; | ||
340 | |||
341 | offset = INSIZE(create); | ||
342 | insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create)); | ||
343 | UPARG(CODA_CREATE); | ||
344 | |||
345 | inp->coda_create.VFid = *dirfid; | ||
346 | inp->coda_create.attr.va_mode = mode; | ||
347 | inp->coda_create.excl = excl; | ||
348 | inp->coda_create.mode = mode; | ||
349 | inp->coda_create.name = offset; | ||
350 | |||
351 | /* Venus must get null terminated string */ | ||
352 | memcpy((char *)(inp) + offset, name, length); | ||
353 | *((char *)inp + offset + length) = '\0'; | ||
354 | |||
355 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
356 | |||
357 | *attrs = outp->coda_create.attr; | ||
358 | *newfid = outp->coda_create.VFid; | ||
359 | |||
360 | CODA_FREE(inp, insize); | ||
361 | return error; | ||
362 | } | ||
363 | |||
364 | int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, | ||
365 | const char *name, int length) | ||
366 | { | ||
367 | union inputArgs *inp; | ||
368 | union outputArgs *outp; | ||
369 | int insize, outsize, error; | ||
370 | int offset; | ||
371 | |||
372 | offset = INSIZE(rmdir); | ||
373 | insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir)); | ||
374 | UPARG(CODA_RMDIR); | ||
375 | |||
376 | inp->coda_rmdir.VFid = *dirfid; | ||
377 | inp->coda_rmdir.name = offset; | ||
378 | memcpy((char *)(inp) + offset, name, length); | ||
379 | *((char *)inp + offset + length) = '\0'; | ||
380 | |||
381 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
382 | |||
383 | CODA_FREE(inp, insize); | ||
384 | return error; | ||
385 | } | ||
386 | |||
387 | int venus_remove(struct super_block *sb, struct CodaFid *dirfid, | ||
388 | const char *name, int length) | ||
389 | { | ||
390 | union inputArgs *inp; | ||
391 | union outputArgs *outp; | ||
392 | int error=0, insize, outsize, offset; | ||
393 | |||
394 | offset = INSIZE(remove); | ||
395 | insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove)); | ||
396 | UPARG(CODA_REMOVE); | ||
397 | |||
398 | inp->coda_remove.VFid = *dirfid; | ||
399 | inp->coda_remove.name = offset; | ||
400 | memcpy((char *)(inp) + offset, name, length); | ||
401 | *((char *)inp + offset + length) = '\0'; | ||
402 | |||
403 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
404 | |||
405 | CODA_FREE(inp, insize); | ||
406 | return error; | ||
407 | } | ||
408 | |||
409 | int venus_readlink(struct super_block *sb, struct CodaFid *fid, | ||
410 | char *buffer, int *length) | ||
411 | { | ||
412 | union inputArgs *inp; | ||
413 | union outputArgs *outp; | ||
414 | int insize, outsize, error; | ||
415 | int retlen; | ||
416 | char *result; | ||
417 | |||
418 | insize = max_t(unsigned int, | ||
419 | INSIZE(readlink), OUTSIZE(readlink)+ *length + 1); | ||
420 | UPARG(CODA_READLINK); | ||
421 | |||
422 | inp->coda_readlink.VFid = *fid; | ||
423 | |||
424 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
425 | |||
426 | if (! error) { | ||
427 | retlen = outp->coda_readlink.count; | ||
428 | if ( retlen > *length ) | ||
429 | retlen = *length; | ||
430 | *length = retlen; | ||
431 | result = (char *)outp + (long)outp->coda_readlink.data; | ||
432 | memcpy(buffer, result, retlen); | ||
433 | *(buffer + retlen) = '\0'; | ||
434 | } | ||
435 | |||
436 | CODA_FREE(inp, insize); | ||
437 | return error; | ||
438 | } | ||
439 | |||
440 | |||
441 | |||
442 | int venus_link(struct super_block *sb, struct CodaFid *fid, | ||
443 | struct CodaFid *dirfid, const char *name, int len ) | ||
444 | { | ||
445 | union inputArgs *inp; | ||
446 | union outputArgs *outp; | ||
447 | int insize, outsize, error; | ||
448 | int offset; | ||
449 | |||
450 | offset = INSIZE(link); | ||
451 | insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link)); | ||
452 | UPARG(CODA_LINK); | ||
453 | |||
454 | inp->coda_link.sourceFid = *fid; | ||
455 | inp->coda_link.destFid = *dirfid; | ||
456 | inp->coda_link.tname = offset; | ||
457 | |||
458 | /* make sure strings are null terminated */ | ||
459 | memcpy((char *)(inp) + offset, name, len); | ||
460 | *((char *)inp + offset + len) = '\0'; | ||
461 | |||
462 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
463 | |||
464 | CODA_FREE(inp, insize); | ||
465 | return error; | ||
466 | } | ||
467 | |||
468 | int venus_symlink(struct super_block *sb, struct CodaFid *fid, | ||
469 | const char *name, int len, | ||
470 | const char *symname, int symlen) | ||
471 | { | ||
472 | union inputArgs *inp; | ||
473 | union outputArgs *outp; | ||
474 | int insize, outsize, error; | ||
475 | int offset, s; | ||
476 | |||
477 | offset = INSIZE(symlink); | ||
478 | insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink)); | ||
479 | UPARG(CODA_SYMLINK); | ||
480 | |||
481 | /* inp->coda_symlink.attr = *tva; XXXXXX */ | ||
482 | inp->coda_symlink.VFid = *fid; | ||
483 | |||
484 | /* Round up to word boundary and null terminate */ | ||
485 | inp->coda_symlink.srcname = offset; | ||
486 | s = ( symlen & ~0x3 ) + 4; | ||
487 | memcpy((char *)(inp) + offset, symname, symlen); | ||
488 | *((char *)inp + offset + symlen) = '\0'; | ||
489 | |||
490 | /* Round up to word boundary and null terminate */ | ||
491 | offset += s; | ||
492 | inp->coda_symlink.tname = offset; | ||
493 | s = (len & ~0x3) + 4; | ||
494 | memcpy((char *)(inp) + offset, name, len); | ||
495 | *((char *)inp + offset + len) = '\0'; | ||
496 | |||
497 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
498 | |||
499 | CODA_FREE(inp, insize); | ||
500 | return error; | ||
501 | } | ||
502 | |||
503 | int venus_fsync(struct super_block *sb, struct CodaFid *fid) | ||
504 | { | ||
505 | union inputArgs *inp; | ||
506 | union outputArgs *outp; | ||
507 | int insize, outsize, error; | ||
508 | |||
509 | insize=SIZE(fsync); | ||
510 | UPARG(CODA_FSYNC); | ||
511 | |||
512 | inp->coda_fsync.VFid = *fid; | ||
513 | error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs), | ||
514 | &outsize, inp); | ||
515 | |||
516 | CODA_FREE(inp, insize); | ||
517 | return error; | ||
518 | } | ||
519 | |||
520 | int venus_access(struct super_block *sb, struct CodaFid *fid, int mask) | ||
521 | { | ||
522 | union inputArgs *inp; | ||
523 | union outputArgs *outp; | ||
524 | int insize, outsize, error; | ||
525 | |||
526 | insize = SIZE(access); | ||
527 | UPARG(CODA_ACCESS); | ||
528 | |||
529 | inp->coda_access.VFid = *fid; | ||
530 | inp->coda_access.flags = mask; | ||
531 | |||
532 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
533 | |||
534 | CODA_FREE(inp, insize); | ||
535 | return error; | ||
536 | } | ||
537 | |||
538 | |||
539 | int venus_pioctl(struct super_block *sb, struct CodaFid *fid, | ||
540 | unsigned int cmd, struct PioctlData *data) | ||
541 | { | ||
542 | union inputArgs *inp; | ||
543 | union outputArgs *outp; | ||
544 | int insize, outsize, error; | ||
545 | int iocsize; | ||
546 | |||
547 | insize = VC_MAXMSGSIZE; | ||
548 | UPARG(CODA_IOCTL); | ||
549 | |||
550 | /* build packet for Venus */ | ||
551 | if (data->vi.in_size > VC_MAXDATASIZE) { | ||
552 | error = -EINVAL; | ||
553 | goto exit; | ||
554 | } | ||
555 | |||
556 | if (data->vi.out_size > VC_MAXDATASIZE) { | ||
557 | error = -EINVAL; | ||
558 | goto exit; | ||
559 | } | ||
560 | |||
561 | inp->coda_ioctl.VFid = *fid; | ||
562 | |||
563 | /* the cmd field was mutated by increasing its size field to | ||
564 | * reflect the path and follow args. We need to subtract that | ||
565 | * out before sending the command to Venus. */ | ||
566 | inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16)); | ||
567 | iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int); | ||
568 | inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16; | ||
569 | |||
570 | /* in->coda_ioctl.rwflag = flag; */ | ||
571 | inp->coda_ioctl.len = data->vi.in_size; | ||
572 | inp->coda_ioctl.data = (char *)(INSIZE(ioctl)); | ||
573 | |||
574 | /* get the data out of user space */ | ||
575 | if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data, | ||
576 | data->vi.in, data->vi.in_size) ) { | ||
577 | error = -EINVAL; | ||
578 | goto exit; | ||
579 | } | ||
580 | |||
581 | error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size, | ||
582 | &outsize, inp); | ||
583 | |||
584 | if (error) { | ||
585 | printk("coda_pioctl: Venus returns: %d for %s\n", | ||
586 | error, coda_f2s(fid)); | ||
587 | goto exit; | ||
588 | } | ||
589 | |||
590 | if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) { | ||
591 | error = -EINVAL; | ||
592 | goto exit; | ||
593 | } | ||
594 | |||
595 | /* Copy out the OUT buffer. */ | ||
596 | if (outp->coda_ioctl.len > data->vi.out_size) { | ||
597 | error = -EINVAL; | ||
598 | goto exit; | ||
599 | } | ||
600 | |||
601 | /* Copy out the OUT buffer. */ | ||
602 | if (copy_to_user(data->vi.out, | ||
603 | (char *)outp + (long)outp->coda_ioctl.data, | ||
604 | outp->coda_ioctl.len)) { | ||
605 | error = -EFAULT; | ||
606 | goto exit; | ||
607 | } | ||
608 | |||
609 | exit: | ||
610 | CODA_FREE(inp, insize); | ||
611 | return error; | ||
612 | } | ||
613 | |||
614 | int venus_statfs(struct super_block *sb, struct kstatfs *sfs) | ||
615 | { | ||
616 | union inputArgs *inp; | ||
617 | union outputArgs *outp; | ||
618 | int insize, outsize, error; | ||
619 | |||
620 | insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs)); | ||
621 | UPARG(CODA_STATFS); | ||
622 | |||
623 | error = coda_upcall(coda_sbp(sb), insize, &outsize, inp); | ||
624 | |||
625 | if (!error) { | ||
626 | sfs->f_blocks = outp->coda_statfs.stat.f_blocks; | ||
627 | sfs->f_bfree = outp->coda_statfs.stat.f_bfree; | ||
628 | sfs->f_bavail = outp->coda_statfs.stat.f_bavail; | ||
629 | sfs->f_files = outp->coda_statfs.stat.f_files; | ||
630 | sfs->f_ffree = outp->coda_statfs.stat.f_ffree; | ||
631 | } else { | ||
632 | printk("coda_statfs: Venus returns: %d\n", error); | ||
633 | } | ||
634 | |||
635 | CODA_FREE(inp, insize); | ||
636 | return error; | ||
637 | } | ||
638 | |||
639 | /* | ||
640 | * coda_upcall and coda_downcall routines. | ||
641 | * | ||
642 | */ | ||
643 | |||
644 | static inline void coda_waitfor_upcall(struct upc_req *vmp, | ||
645 | struct venus_comm *vcommp) | ||
646 | { | ||
647 | DECLARE_WAITQUEUE(wait, current); | ||
648 | |||
649 | vmp->uc_posttime = jiffies; | ||
650 | |||
651 | add_wait_queue(&vmp->uc_sleep, &wait); | ||
652 | for (;;) { | ||
653 | if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE ) | ||
654 | set_current_state(TASK_INTERRUPTIBLE); | ||
655 | else | ||
656 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
657 | |||
658 | /* venus died */ | ||
659 | if ( !vcommp->vc_inuse ) | ||
660 | break; | ||
661 | |||
662 | /* got a reply */ | ||
663 | if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) ) | ||
664 | break; | ||
665 | |||
666 | if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) { | ||
667 | /* if this process really wants to die, let it go */ | ||
668 | if ( sigismember(&(current->pending.signal), SIGKILL) || | ||
669 | sigismember(&(current->pending.signal), SIGINT) ) | ||
670 | break; | ||
671 | /* signal is present: after timeout always return | ||
672 | really smart idea, probably useless ... */ | ||
673 | if ( jiffies - vmp->uc_posttime > coda_timeout * HZ ) | ||
674 | break; | ||
675 | } | ||
676 | schedule(); | ||
677 | } | ||
678 | remove_wait_queue(&vmp->uc_sleep, &wait); | ||
679 | set_current_state(TASK_RUNNING); | ||
680 | |||
681 | return; | ||
682 | } | ||
683 | |||
684 | |||
685 | /* | ||
686 | * coda_upcall will return an error in the case of | ||
687 | * failed communication with Venus _or_ will peek at Venus | ||
688 | * reply and return Venus' error. | ||
689 | * | ||
690 | * As venus has 2 types of errors, normal errors (positive) and internal | ||
691 | * errors (negative), normal errors are negated, while internal errors | ||
692 | * are all mapped to -EINTR, while showing a nice warning message. (jh) | ||
693 | * | ||
694 | */ | ||
695 | static int coda_upcall(struct coda_sb_info *sbi, | ||
696 | int inSize, int *outSize, | ||
697 | union inputArgs *buffer) | ||
698 | { | ||
699 | struct venus_comm *vcommp; | ||
700 | union outputArgs *out; | ||
701 | struct upc_req *req; | ||
702 | int error = 0; | ||
703 | |||
704 | vcommp = sbi->sbi_vcomm; | ||
705 | if ( !vcommp->vc_inuse ) { | ||
706 | printk("No pseudo device in upcall comms at %p\n", vcommp); | ||
707 | return -ENXIO; | ||
708 | } | ||
709 | |||
710 | /* Format the request message. */ | ||
711 | req = upc_alloc(); | ||
712 | if (!req) { | ||
713 | printk("Failed to allocate upc_req structure\n"); | ||
714 | return -ENOMEM; | ||
715 | } | ||
716 | req->uc_data = (void *)buffer; | ||
717 | req->uc_flags = 0; | ||
718 | req->uc_inSize = inSize; | ||
719 | req->uc_outSize = *outSize ? *outSize : inSize; | ||
720 | req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode; | ||
721 | req->uc_unique = ++vcommp->vc_seq; | ||
722 | init_waitqueue_head(&req->uc_sleep); | ||
723 | |||
724 | /* Fill in the common input args. */ | ||
725 | ((union inputArgs *)buffer)->ih.unique = req->uc_unique; | ||
726 | |||
727 | /* Append msg to pending queue and poke Venus. */ | ||
728 | list_add(&(req->uc_chain), vcommp->vc_pending.prev); | ||
729 | |||
730 | wake_up_interruptible(&vcommp->vc_waitq); | ||
731 | /* We can be interrupted while we wait for Venus to process | ||
732 | * our request. If the interrupt occurs before Venus has read | ||
733 | * the request, we dequeue and return. If it occurs after the | ||
734 | * read but before the reply, we dequeue, send a signal | ||
735 | * message, and return. If it occurs after the reply we ignore | ||
736 | * it. In no case do we want to restart the syscall. If it | ||
737 | * was interrupted by a venus shutdown (psdev_close), return | ||
738 | * ENODEV. */ | ||
739 | |||
740 | /* Go to sleep. Wake up on signals only after the timeout. */ | ||
741 | coda_waitfor_upcall(req, vcommp); | ||
742 | |||
743 | if (vcommp->vc_inuse) { /* i.e. Venus is still alive */ | ||
744 | /* Op went through, interrupt or not... */ | ||
745 | if (req->uc_flags & REQ_WRITE) { | ||
746 | out = (union outputArgs *)req->uc_data; | ||
747 | /* here we map positive Venus errors to kernel errors */ | ||
748 | error = -out->oh.result; | ||
749 | *outSize = req->uc_outSize; | ||
750 | goto exit; | ||
751 | } | ||
752 | if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) { | ||
753 | /* Interrupted before venus read it. */ | ||
754 | list_del(&(req->uc_chain)); | ||
755 | /* perhaps the best way to convince the app to | ||
756 | give up? */ | ||
757 | error = -EINTR; | ||
758 | goto exit; | ||
759 | } | ||
760 | if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) { | ||
761 | /* interrupted after Venus did its read, send signal */ | ||
762 | union inputArgs *sig_inputArgs; | ||
763 | struct upc_req *sig_req; | ||
764 | |||
765 | list_del(&(req->uc_chain)); | ||
766 | error = -ENOMEM; | ||
767 | sig_req = upc_alloc(); | ||
768 | if (!sig_req) goto exit; | ||
769 | |||
770 | CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr)); | ||
771 | if (!sig_req->uc_data) { | ||
772 | upc_free(sig_req); | ||
773 | goto exit; | ||
774 | } | ||
775 | |||
776 | error = -EINTR; | ||
777 | sig_inputArgs = (union inputArgs *)sig_req->uc_data; | ||
778 | sig_inputArgs->ih.opcode = CODA_SIGNAL; | ||
779 | sig_inputArgs->ih.unique = req->uc_unique; | ||
780 | |||
781 | sig_req->uc_flags = REQ_ASYNC; | ||
782 | sig_req->uc_opcode = sig_inputArgs->ih.opcode; | ||
783 | sig_req->uc_unique = sig_inputArgs->ih.unique; | ||
784 | sig_req->uc_inSize = sizeof(struct coda_in_hdr); | ||
785 | sig_req->uc_outSize = sizeof(struct coda_in_hdr); | ||
786 | |||
787 | /* insert at head of queue! */ | ||
788 | list_add(&(sig_req->uc_chain), &vcommp->vc_pending); | ||
789 | wake_up_interruptible(&vcommp->vc_waitq); | ||
790 | } else { | ||
791 | printk("Coda: Strange interruption..\n"); | ||
792 | error = -EINTR; | ||
793 | } | ||
794 | } else { /* If venus died i.e. !VC_OPEN(vcommp) */ | ||
795 | printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n", | ||
796 | req->uc_opcode, req->uc_unique, req->uc_flags); | ||
797 | error = -ENODEV; | ||
798 | } | ||
799 | |||
800 | exit: | ||
801 | upc_free(req); | ||
802 | return error; | ||
803 | } | ||
804 | |||
805 | /* | ||
806 | The statements below are part of the Coda opportunistic | ||
807 | programming -- taken from the Mach/BSD kernel code for Coda. | ||
808 | You don't get correct semantics by stating what needs to be | ||
809 | done without guaranteeing the invariants needed for it to happen. | ||
810 | When will be have time to find out what exactly is going on? (pjb) | ||
811 | */ | ||
812 | |||
813 | |||
814 | /* | ||
815 | * There are 7 cases where cache invalidations occur. The semantics | ||
816 | * of each is listed here: | ||
817 | * | ||
818 | * CODA_FLUSH -- flush all entries from the name cache and the cnode cache. | ||
819 | * CODA_PURGEUSER -- flush all entries from the name cache for a specific user | ||
820 | * This call is a result of token expiration. | ||
821 | * | ||
822 | * The next arise as the result of callbacks on a file or directory. | ||
823 | * CODA_ZAPFILE -- flush the cached attributes for a file. | ||
824 | |||
825 | * CODA_ZAPDIR -- flush the attributes for the dir and | ||
826 | * force a new lookup for all the children | ||
827 | of this dir. | ||
828 | |||
829 | * | ||
830 | * The next is a result of Venus detecting an inconsistent file. | ||
831 | * CODA_PURGEFID -- flush the attribute for the file | ||
832 | * purge it and its children from the dcache | ||
833 | * | ||
834 | * The last allows Venus to replace local fids with global ones | ||
835 | * during reintegration. | ||
836 | * | ||
837 | * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */ | ||
838 | |||
839 | int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb) | ||
840 | { | ||
841 | /* Handle invalidation requests. */ | ||
842 | if ( !sb || !sb->s_root || !sb->s_root->d_inode) | ||
843 | return 0; | ||
844 | |||
845 | switch (opcode) { | ||
846 | |||
847 | case CODA_FLUSH : { | ||
848 | coda_cache_clear_all(sb); | ||
849 | shrink_dcache_sb(sb); | ||
850 | coda_flag_inode(sb->s_root->d_inode, C_FLUSH); | ||
851 | return(0); | ||
852 | } | ||
853 | |||
854 | case CODA_PURGEUSER : { | ||
855 | coda_cache_clear_all(sb); | ||
856 | return(0); | ||
857 | } | ||
858 | |||
859 | case CODA_ZAPDIR : { | ||
860 | struct inode *inode; | ||
861 | struct CodaFid *fid = &out->coda_zapdir.CodaFid; | ||
862 | |||
863 | inode = coda_fid_to_inode(fid, sb); | ||
864 | if (inode) { | ||
865 | coda_flag_inode_children(inode, C_PURGE); | ||
866 | coda_flag_inode(inode, C_VATTR); | ||
867 | iput(inode); | ||
868 | } | ||
869 | |||
870 | return(0); | ||
871 | } | ||
872 | |||
873 | case CODA_ZAPFILE : { | ||
874 | struct inode *inode; | ||
875 | struct CodaFid *fid = &out->coda_zapfile.CodaFid; | ||
876 | inode = coda_fid_to_inode(fid, sb); | ||
877 | if ( inode ) { | ||
878 | coda_flag_inode(inode, C_VATTR); | ||
879 | iput(inode); | ||
880 | } | ||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | case CODA_PURGEFID : { | ||
885 | struct inode *inode; | ||
886 | struct CodaFid *fid = &out->coda_purgefid.CodaFid; | ||
887 | inode = coda_fid_to_inode(fid, sb); | ||
888 | if ( inode ) { | ||
889 | coda_flag_inode_children(inode, C_PURGE); | ||
890 | |||
891 | /* catch the dentries later if some are still busy */ | ||
892 | coda_flag_inode(inode, C_PURGE); | ||
893 | d_prune_aliases(inode); | ||
894 | |||
895 | iput(inode); | ||
896 | } | ||
897 | return 0; | ||
898 | } | ||
899 | |||
900 | case CODA_REPLACE : { | ||
901 | struct inode *inode; | ||
902 | struct CodaFid *oldfid = &out->coda_replace.OldFid; | ||
903 | struct CodaFid *newfid = &out->coda_replace.NewFid; | ||
904 | inode = coda_fid_to_inode(oldfid, sb); | ||
905 | if ( inode ) { | ||
906 | coda_replace_fid(inode, oldfid, newfid); | ||
907 | iput(inode); | ||
908 | } | ||
909 | return 0; | ||
910 | } | ||
911 | } | ||
912 | return 0; | ||
913 | } | ||
914 | |||