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/ncpfs |
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/ncpfs')
-rw-r--r-- | fs/ncpfs/Kconfig | 87 | ||||
-rw-r--r-- | fs/ncpfs/Makefile | 16 | ||||
-rw-r--r-- | fs/ncpfs/dir.c | 1260 | ||||
-rw-r--r-- | fs/ncpfs/file.c | 300 | ||||
-rw-r--r-- | fs/ncpfs/getopt.c | 75 | ||||
-rw-r--r-- | fs/ncpfs/getopt.h | 16 | ||||
-rw-r--r-- | fs/ncpfs/inode.c | 1012 | ||||
-rw-r--r-- | fs/ncpfs/ioctl.c | 649 | ||||
-rw-r--r-- | fs/ncpfs/mmap.c | 128 | ||||
-rw-r--r-- | fs/ncpfs/ncplib_kernel.c | 1355 | ||||
-rw-r--r-- | fs/ncpfs/ncplib_kernel.h | 259 | ||||
-rw-r--r-- | fs/ncpfs/ncpsign_kernel.c | 127 | ||||
-rw-r--r-- | fs/ncpfs/ncpsign_kernel.h | 28 | ||||
-rw-r--r-- | fs/ncpfs/sock.c | 850 | ||||
-rw-r--r-- | fs/ncpfs/symlink.c | 183 |
15 files changed, 6345 insertions, 0 deletions
diff --git a/fs/ncpfs/Kconfig b/fs/ncpfs/Kconfig new file mode 100644 index 000000000000..142808427b25 --- /dev/null +++ b/fs/ncpfs/Kconfig | |||
@@ -0,0 +1,87 @@ | |||
1 | # | ||
2 | # NCP Filesystem configuration | ||
3 | # | ||
4 | config NCPFS_PACKET_SIGNING | ||
5 | bool "Packet signatures" | ||
6 | depends on NCP_FS | ||
7 | help | ||
8 | NCP allows packets to be signed for stronger security. If you want | ||
9 | security, say Y. Normal users can leave it off. To be able to use | ||
10 | packet signing you must use ncpfs > 2.0.12. | ||
11 | |||
12 | config NCPFS_IOCTL_LOCKING | ||
13 | bool "Proprietary file locking" | ||
14 | depends on NCP_FS | ||
15 | help | ||
16 | Allows locking of records on remote volumes. Say N unless you have | ||
17 | special applications which are able to utilize this locking scheme. | ||
18 | |||
19 | config NCPFS_STRONG | ||
20 | bool "Clear remove/delete inhibit when needed" | ||
21 | depends on NCP_FS | ||
22 | help | ||
23 | Allows manipulation of files flagged as Delete or Rename Inhibit. | ||
24 | To use this feature you must mount volumes with the ncpmount | ||
25 | parameter "-s" (ncpfs-2.0.12 and newer). Say Y unless you are not | ||
26 | mounting volumes with -f 444. | ||
27 | |||
28 | config NCPFS_NFS_NS | ||
29 | bool "Use NFS namespace if available" | ||
30 | depends on NCP_FS | ||
31 | help | ||
32 | Allows you to utilize NFS namespace on NetWare servers. It brings | ||
33 | you case sensitive filenames. Say Y. You can disable it at | ||
34 | mount-time with the `-N nfs' parameter of ncpmount. | ||
35 | |||
36 | config NCPFS_OS2_NS | ||
37 | bool "Use LONG (OS/2) namespace if available" | ||
38 | depends on NCP_FS | ||
39 | help | ||
40 | Allows you to utilize OS2/LONG namespace on NetWare servers. | ||
41 | Filenames in this namespace are limited to 255 characters, they are | ||
42 | case insensitive, and case in names is preserved. Say Y. You can | ||
43 | disable it at mount time with the -N os2 parameter of ncpmount. | ||
44 | |||
45 | config NCPFS_SMALLDOS | ||
46 | bool "Lowercase DOS filenames" | ||
47 | depends on NCP_FS | ||
48 | ---help--- | ||
49 | If you say Y here, every filename on a NetWare server volume using | ||
50 | the OS2/LONG namespace and created under DOS or on a volume using | ||
51 | DOS namespace will be converted to lowercase characters. | ||
52 | Saying N here will give you these filenames in uppercase. | ||
53 | |||
54 | This is only a cosmetic option since the OS2/LONG namespace is case | ||
55 | insensitive. The only major reason for this option is backward | ||
56 | compatibility when moving from DOS to OS2/LONG namespace support. | ||
57 | Long filenames (created by Win95) will not be affected. | ||
58 | |||
59 | This option does not solve the problem that filenames appear | ||
60 | differently under Linux and under Windows, since Windows does an | ||
61 | additional conversions on the client side. You can achieve similar | ||
62 | effects by saying Y to "Allow using of Native Language Support" | ||
63 | below. | ||
64 | |||
65 | config NCPFS_NLS | ||
66 | bool "Use Native Language Support" | ||
67 | depends on NCP_FS | ||
68 | select NLS | ||
69 | help | ||
70 | Allows you to use codepages and I/O charsets for file name | ||
71 | translation between the server file system and input/output. This | ||
72 | may be useful, if you want to access the server with other operating | ||
73 | systems, e.g. Windows 95. See also NLS for more Information. | ||
74 | |||
75 | To select codepages and I/O charsets use ncpfs-2.2.0.13 or newer. | ||
76 | |||
77 | config NCPFS_EXTRAS | ||
78 | bool "Enable symbolic links and execute flags" | ||
79 | depends on NCP_FS | ||
80 | help | ||
81 | This enables the use of symbolic links and an execute permission | ||
82 | bit on NCPFS. The file server need not have long name space or NFS | ||
83 | name space loaded for these to work. | ||
84 | |||
85 | To use the new attributes, it is recommended to use the flags | ||
86 | '-f 600 -d 755' on the ncpmount command line. | ||
87 | |||
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile new file mode 100644 index 000000000000..68ea095100a8 --- /dev/null +++ b/fs/ncpfs/Makefile | |||
@@ -0,0 +1,16 @@ | |||
1 | # | ||
2 | # Makefile for the linux ncp filesystem routines. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_NCP_FS) += ncpfs.o | ||
6 | |||
7 | ncpfs-y := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \ | ||
8 | ncpsign_kernel.o getopt.o | ||
9 | |||
10 | ncpfs-$(CONFIG_NCPFS_EXTRAS) += symlink.o | ||
11 | ncpfs-$(CONFIG_NCPFS_NFS_NS) += symlink.o | ||
12 | |||
13 | # If you want debugging output, please uncomment the following line | ||
14 | # EXTRA_CFLAGS += -DDEBUG_NCP=1 | ||
15 | |||
16 | CFLAGS_ncplib_kernel.o := -finline-functions | ||
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c new file mode 100644 index 000000000000..2dc2d8693968 --- /dev/null +++ b/fs/ncpfs/dir.c | |||
@@ -0,0 +1,1260 @@ | |||
1 | /* | ||
2 | * dir.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 by Volker Lendecke | ||
5 | * Modified for big endian by J.F. Chadima and David S. Miller | ||
6 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
7 | * Modified 1998, 1999 Wolfram Pienkoss for NLS | ||
8 | * Modified 1999 Wolfram Pienkoss for directory caching | ||
9 | * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | |||
15 | #include <linux/time.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/stat.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/vmalloc.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/byteorder.h> | ||
24 | #include <linux/smp_lock.h> | ||
25 | |||
26 | #include <linux/ncp_fs.h> | ||
27 | |||
28 | #include "ncplib_kernel.h" | ||
29 | |||
30 | static void ncp_read_volume_list(struct file *, void *, filldir_t, | ||
31 | struct ncp_cache_control *); | ||
32 | static void ncp_do_readdir(struct file *, void *, filldir_t, | ||
33 | struct ncp_cache_control *); | ||
34 | |||
35 | static int ncp_readdir(struct file *, void *, filldir_t); | ||
36 | |||
37 | static int ncp_create(struct inode *, struct dentry *, int, struct nameidata *); | ||
38 | static struct dentry *ncp_lookup(struct inode *, struct dentry *, struct nameidata *); | ||
39 | static int ncp_unlink(struct inode *, struct dentry *); | ||
40 | static int ncp_mkdir(struct inode *, struct dentry *, int); | ||
41 | static int ncp_rmdir(struct inode *, struct dentry *); | ||
42 | static int ncp_rename(struct inode *, struct dentry *, | ||
43 | struct inode *, struct dentry *); | ||
44 | static int ncp_mknod(struct inode * dir, struct dentry *dentry, | ||
45 | int mode, dev_t rdev); | ||
46 | #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) | ||
47 | extern int ncp_symlink(struct inode *, struct dentry *, const char *); | ||
48 | #else | ||
49 | #define ncp_symlink NULL | ||
50 | #endif | ||
51 | |||
52 | struct file_operations ncp_dir_operations = | ||
53 | { | ||
54 | .read = generic_read_dir, | ||
55 | .readdir = ncp_readdir, | ||
56 | .ioctl = ncp_ioctl, | ||
57 | }; | ||
58 | |||
59 | struct inode_operations ncp_dir_inode_operations = | ||
60 | { | ||
61 | .create = ncp_create, | ||
62 | .lookup = ncp_lookup, | ||
63 | .unlink = ncp_unlink, | ||
64 | .symlink = ncp_symlink, | ||
65 | .mkdir = ncp_mkdir, | ||
66 | .rmdir = ncp_rmdir, | ||
67 | .mknod = ncp_mknod, | ||
68 | .rename = ncp_rename, | ||
69 | .setattr = ncp_notify_change, | ||
70 | }; | ||
71 | |||
72 | /* | ||
73 | * Dentry operations routines | ||
74 | */ | ||
75 | static int ncp_lookup_validate(struct dentry *, struct nameidata *); | ||
76 | static int ncp_hash_dentry(struct dentry *, struct qstr *); | ||
77 | static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *); | ||
78 | static int ncp_delete_dentry(struct dentry *); | ||
79 | |||
80 | static struct dentry_operations ncp_dentry_operations = | ||
81 | { | ||
82 | .d_revalidate = ncp_lookup_validate, | ||
83 | .d_hash = ncp_hash_dentry, | ||
84 | .d_compare = ncp_compare_dentry, | ||
85 | .d_delete = ncp_delete_dentry, | ||
86 | }; | ||
87 | |||
88 | struct dentry_operations ncp_root_dentry_operations = | ||
89 | { | ||
90 | .d_hash = ncp_hash_dentry, | ||
91 | .d_compare = ncp_compare_dentry, | ||
92 | .d_delete = ncp_delete_dentry, | ||
93 | }; | ||
94 | |||
95 | |||
96 | /* | ||
97 | * Note: leave the hash unchanged if the directory | ||
98 | * is case-sensitive. | ||
99 | */ | ||
100 | static int | ||
101 | ncp_hash_dentry(struct dentry *dentry, struct qstr *this) | ||
102 | { | ||
103 | struct nls_table *t; | ||
104 | unsigned long hash; | ||
105 | int i; | ||
106 | |||
107 | t = NCP_IO_TABLE(dentry); | ||
108 | |||
109 | if (!ncp_case_sensitive(dentry->d_inode)) { | ||
110 | hash = init_name_hash(); | ||
111 | for (i=0; i<this->len ; i++) | ||
112 | hash = partial_name_hash(ncp_tolower(t, this->name[i]), | ||
113 | hash); | ||
114 | this->hash = end_name_hash(hash); | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int | ||
120 | ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) | ||
121 | { | ||
122 | if (a->len != b->len) | ||
123 | return 1; | ||
124 | |||
125 | if (ncp_case_sensitive(dentry->d_inode)) | ||
126 | return strncmp(a->name, b->name, a->len); | ||
127 | |||
128 | return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len); | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * This is the callback from dput() when d_count is going to 0. | ||
133 | * We use this to unhash dentries with bad inodes. | ||
134 | * Closing files can be safely postponed until iput() - it's done there anyway. | ||
135 | */ | ||
136 | static int | ||
137 | ncp_delete_dentry(struct dentry * dentry) | ||
138 | { | ||
139 | struct inode *inode = dentry->d_inode; | ||
140 | |||
141 | if (inode) { | ||
142 | if (is_bad_inode(inode)) | ||
143 | return 1; | ||
144 | } else | ||
145 | { | ||
146 | /* N.B. Unhash negative dentries? */ | ||
147 | } | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static inline int | ||
152 | ncp_single_volume(struct ncp_server *server) | ||
153 | { | ||
154 | return (server->m.mounted_vol[0] != '\0'); | ||
155 | } | ||
156 | |||
157 | static inline int ncp_is_server_root(struct inode *inode) | ||
158 | { | ||
159 | return (!ncp_single_volume(NCP_SERVER(inode)) && | ||
160 | inode == inode->i_sb->s_root->d_inode); | ||
161 | } | ||
162 | |||
163 | |||
164 | /* | ||
165 | * This is the callback when the dcache has a lookup hit. | ||
166 | */ | ||
167 | |||
168 | |||
169 | #ifdef CONFIG_NCPFS_STRONG | ||
170 | /* try to delete a readonly file (NW R bit set) */ | ||
171 | |||
172 | static int | ||
173 | ncp_force_unlink(struct inode *dir, struct dentry* dentry) | ||
174 | { | ||
175 | int res=0x9c,res2; | ||
176 | struct nw_modify_dos_info info; | ||
177 | __le32 old_nwattr; | ||
178 | struct inode *inode; | ||
179 | |||
180 | memset(&info, 0, sizeof(info)); | ||
181 | |||
182 | /* remove the Read-Only flag on the NW server */ | ||
183 | inode = dentry->d_inode; | ||
184 | |||
185 | old_nwattr = NCP_FINFO(inode)->nwattr; | ||
186 | info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT); | ||
187 | res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info); | ||
188 | if (res2) | ||
189 | goto leave_me; | ||
190 | |||
191 | /* now try again the delete operation */ | ||
192 | res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry); | ||
193 | |||
194 | if (res) /* delete failed, set R bit again */ | ||
195 | { | ||
196 | info.attributes = old_nwattr; | ||
197 | res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info); | ||
198 | if (res2) | ||
199 | goto leave_me; | ||
200 | } | ||
201 | leave_me: | ||
202 | return(res); | ||
203 | } | ||
204 | #endif /* CONFIG_NCPFS_STRONG */ | ||
205 | |||
206 | #ifdef CONFIG_NCPFS_STRONG | ||
207 | static int | ||
208 | ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name, | ||
209 | struct inode *new_dir, struct dentry* new_dentry, char *_new_name) | ||
210 | { | ||
211 | struct nw_modify_dos_info info; | ||
212 | int res=0x90,res2; | ||
213 | struct inode *old_inode = old_dentry->d_inode; | ||
214 | __le32 old_nwattr = NCP_FINFO(old_inode)->nwattr; | ||
215 | __le32 new_nwattr = 0; /* shut compiler warning */ | ||
216 | int old_nwattr_changed = 0; | ||
217 | int new_nwattr_changed = 0; | ||
218 | |||
219 | memset(&info, 0, sizeof(info)); | ||
220 | |||
221 | /* remove the Read-Only flag on the NW server */ | ||
222 | |||
223 | info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); | ||
224 | res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info); | ||
225 | if (!res2) | ||
226 | old_nwattr_changed = 1; | ||
227 | if (new_dentry && new_dentry->d_inode) { | ||
228 | new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr; | ||
229 | info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); | ||
230 | res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info); | ||
231 | if (!res2) | ||
232 | new_nwattr_changed = 1; | ||
233 | } | ||
234 | /* now try again the rename operation */ | ||
235 | /* but only if something really happened */ | ||
236 | if (new_nwattr_changed || old_nwattr_changed) { | ||
237 | res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), | ||
238 | old_dir, _old_name, | ||
239 | new_dir, _new_name); | ||
240 | } | ||
241 | if (res) | ||
242 | goto leave_me; | ||
243 | /* file was successfully renamed, so: | ||
244 | do not set attributes on old file - it no longer exists | ||
245 | copy attributes from old file to new */ | ||
246 | new_nwattr_changed = old_nwattr_changed; | ||
247 | new_nwattr = old_nwattr; | ||
248 | old_nwattr_changed = 0; | ||
249 | |||
250 | leave_me:; | ||
251 | if (old_nwattr_changed) { | ||
252 | info.attributes = old_nwattr; | ||
253 | res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info); | ||
254 | /* ignore errors */ | ||
255 | } | ||
256 | if (new_nwattr_changed) { | ||
257 | info.attributes = new_nwattr; | ||
258 | res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info); | ||
259 | /* ignore errors */ | ||
260 | } | ||
261 | return(res); | ||
262 | } | ||
263 | #endif /* CONFIG_NCPFS_STRONG */ | ||
264 | |||
265 | |||
266 | static int | ||
267 | __ncp_lookup_validate(struct dentry * dentry, struct nameidata *nd) | ||
268 | { | ||
269 | struct ncp_server *server; | ||
270 | struct dentry *parent; | ||
271 | struct inode *dir; | ||
272 | struct ncp_entry_info finfo; | ||
273 | int res, val = 0, len; | ||
274 | __u8 __name[NCP_MAXPATHLEN + 1]; | ||
275 | |||
276 | parent = dget_parent(dentry); | ||
277 | dir = parent->d_inode; | ||
278 | |||
279 | if (!dentry->d_inode) | ||
280 | goto finished; | ||
281 | |||
282 | server = NCP_SERVER(dir); | ||
283 | |||
284 | if (!ncp_conn_valid(server)) | ||
285 | goto finished; | ||
286 | |||
287 | /* | ||
288 | * Inspired by smbfs: | ||
289 | * The default validation is based on dentry age: | ||
290 | * We set the max age at mount time. (But each | ||
291 | * successful server lookup renews the timestamp.) | ||
292 | */ | ||
293 | val = NCP_TEST_AGE(server, dentry); | ||
294 | if (val) | ||
295 | goto finished; | ||
296 | |||
297 | DDPRINTK("ncp_lookup_validate: %s/%s not valid, age=%ld, server lookup\n", | ||
298 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
299 | NCP_GET_AGE(dentry)); | ||
300 | |||
301 | len = sizeof(__name); | ||
302 | if (ncp_is_server_root(dir)) { | ||
303 | res = ncp_io2vol(server, __name, &len, dentry->d_name.name, | ||
304 | dentry->d_name.len, 1); | ||
305 | if (!res) | ||
306 | res = ncp_lookup_volume(server, __name, &(finfo.i)); | ||
307 | } else { | ||
308 | res = ncp_io2vol(server, __name, &len, dentry->d_name.name, | ||
309 | dentry->d_name.len, !ncp_preserve_case(dir)); | ||
310 | if (!res) | ||
311 | res = ncp_obtain_info(server, dir, __name, &(finfo.i)); | ||
312 | } | ||
313 | finfo.volume = finfo.i.volNumber; | ||
314 | DDPRINTK("ncp_lookup_validate: looked for %s/%s, res=%d\n", | ||
315 | dentry->d_parent->d_name.name, __name, res); | ||
316 | /* | ||
317 | * If we didn't find it, or if it has a different dirEntNum to | ||
318 | * what we remember, it's not valid any more. | ||
319 | */ | ||
320 | if (!res) { | ||
321 | if (finfo.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum) { | ||
322 | ncp_new_dentry(dentry); | ||
323 | val=1; | ||
324 | } else | ||
325 | DDPRINTK("ncp_lookup_validate: found, but dirEntNum changed\n"); | ||
326 | |||
327 | ncp_update_inode2(dentry->d_inode, &finfo); | ||
328 | } | ||
329 | |||
330 | finished: | ||
331 | DDPRINTK("ncp_lookup_validate: result=%d\n", val); | ||
332 | dput(parent); | ||
333 | return val; | ||
334 | } | ||
335 | |||
336 | static int | ||
337 | ncp_lookup_validate(struct dentry * dentry, struct nameidata *nd) | ||
338 | { | ||
339 | int res; | ||
340 | lock_kernel(); | ||
341 | res = __ncp_lookup_validate(dentry, nd); | ||
342 | unlock_kernel(); | ||
343 | return res; | ||
344 | } | ||
345 | |||
346 | static struct dentry * | ||
347 | ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos) | ||
348 | { | ||
349 | struct dentry *dent = dentry; | ||
350 | struct list_head *next; | ||
351 | |||
352 | if (d_validate(dent, parent)) { | ||
353 | if (dent->d_name.len <= NCP_MAXPATHLEN && | ||
354 | (unsigned long)dent->d_fsdata == fpos) { | ||
355 | if (!dent->d_inode) { | ||
356 | dput(dent); | ||
357 | dent = NULL; | ||
358 | } | ||
359 | return dent; | ||
360 | } | ||
361 | dput(dent); | ||
362 | } | ||
363 | |||
364 | /* If a pointer is invalid, we search the dentry. */ | ||
365 | spin_lock(&dcache_lock); | ||
366 | next = parent->d_subdirs.next; | ||
367 | while (next != &parent->d_subdirs) { | ||
368 | dent = list_entry(next, struct dentry, d_child); | ||
369 | if ((unsigned long)dent->d_fsdata == fpos) { | ||
370 | if (dent->d_inode) | ||
371 | dget_locked(dent); | ||
372 | else | ||
373 | dent = NULL; | ||
374 | spin_unlock(&dcache_lock); | ||
375 | goto out; | ||
376 | } | ||
377 | next = next->next; | ||
378 | } | ||
379 | spin_unlock(&dcache_lock); | ||
380 | return NULL; | ||
381 | |||
382 | out: | ||
383 | return dent; | ||
384 | } | ||
385 | |||
386 | static time_t ncp_obtain_mtime(struct dentry *dentry) | ||
387 | { | ||
388 | struct inode *inode = dentry->d_inode; | ||
389 | struct ncp_server *server = NCP_SERVER(inode); | ||
390 | struct nw_info_struct i; | ||
391 | |||
392 | if (!ncp_conn_valid(server) || ncp_is_server_root(inode)) | ||
393 | return 0; | ||
394 | |||
395 | if (ncp_obtain_info(server, inode, NULL, &i)) | ||
396 | return 0; | ||
397 | |||
398 | return ncp_date_dos2unix(i.modifyTime, i.modifyDate); | ||
399 | } | ||
400 | |||
401 | static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
402 | { | ||
403 | struct dentry *dentry = filp->f_dentry; | ||
404 | struct inode *inode = dentry->d_inode; | ||
405 | struct page *page = NULL; | ||
406 | struct ncp_server *server = NCP_SERVER(inode); | ||
407 | union ncp_dir_cache *cache = NULL; | ||
408 | struct ncp_cache_control ctl; | ||
409 | int result, mtime_valid = 0; | ||
410 | time_t mtime = 0; | ||
411 | |||
412 | lock_kernel(); | ||
413 | |||
414 | ctl.page = NULL; | ||
415 | ctl.cache = NULL; | ||
416 | |||
417 | DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n", | ||
418 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
419 | (int) filp->f_pos); | ||
420 | |||
421 | result = -EIO; | ||
422 | if (!ncp_conn_valid(server)) | ||
423 | goto out; | ||
424 | |||
425 | result = 0; | ||
426 | if (filp->f_pos == 0) { | ||
427 | if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR)) | ||
428 | goto out; | ||
429 | filp->f_pos = 1; | ||
430 | } | ||
431 | if (filp->f_pos == 1) { | ||
432 | if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR)) | ||
433 | goto out; | ||
434 | filp->f_pos = 2; | ||
435 | } | ||
436 | |||
437 | page = grab_cache_page(&inode->i_data, 0); | ||
438 | if (!page) | ||
439 | goto read_really; | ||
440 | |||
441 | ctl.cache = cache = kmap(page); | ||
442 | ctl.head = cache->head; | ||
443 | |||
444 | if (!PageUptodate(page) || !ctl.head.eof) | ||
445 | goto init_cache; | ||
446 | |||
447 | if (filp->f_pos == 2) { | ||
448 | if (jiffies - ctl.head.time >= NCP_MAX_AGE(server)) | ||
449 | goto init_cache; | ||
450 | |||
451 | mtime = ncp_obtain_mtime(dentry); | ||
452 | mtime_valid = 1; | ||
453 | if ((!mtime) || (mtime != ctl.head.mtime)) | ||
454 | goto init_cache; | ||
455 | } | ||
456 | |||
457 | if (filp->f_pos > ctl.head.end) | ||
458 | goto finished; | ||
459 | |||
460 | ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2); | ||
461 | ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE; | ||
462 | ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE; | ||
463 | |||
464 | for (;;) { | ||
465 | if (ctl.ofs != 0) { | ||
466 | ctl.page = find_lock_page(&inode->i_data, ctl.ofs); | ||
467 | if (!ctl.page) | ||
468 | goto invalid_cache; | ||
469 | ctl.cache = kmap(ctl.page); | ||
470 | if (!PageUptodate(ctl.page)) | ||
471 | goto invalid_cache; | ||
472 | } | ||
473 | while (ctl.idx < NCP_DIRCACHE_SIZE) { | ||
474 | struct dentry *dent; | ||
475 | int res; | ||
476 | |||
477 | dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx], | ||
478 | dentry, filp->f_pos); | ||
479 | if (!dent) | ||
480 | goto invalid_cache; | ||
481 | res = filldir(dirent, dent->d_name.name, | ||
482 | dent->d_name.len, filp->f_pos, | ||
483 | dent->d_inode->i_ino, DT_UNKNOWN); | ||
484 | dput(dent); | ||
485 | if (res) | ||
486 | goto finished; | ||
487 | filp->f_pos += 1; | ||
488 | ctl.idx += 1; | ||
489 | if (filp->f_pos > ctl.head.end) | ||
490 | goto finished; | ||
491 | } | ||
492 | if (ctl.page) { | ||
493 | kunmap(ctl.page); | ||
494 | SetPageUptodate(ctl.page); | ||
495 | unlock_page(ctl.page); | ||
496 | page_cache_release(ctl.page); | ||
497 | ctl.page = NULL; | ||
498 | } | ||
499 | ctl.idx = 0; | ||
500 | ctl.ofs += 1; | ||
501 | } | ||
502 | invalid_cache: | ||
503 | if (ctl.page) { | ||
504 | kunmap(ctl.page); | ||
505 | unlock_page(ctl.page); | ||
506 | page_cache_release(ctl.page); | ||
507 | ctl.page = NULL; | ||
508 | } | ||
509 | ctl.cache = cache; | ||
510 | init_cache: | ||
511 | ncp_invalidate_dircache_entries(dentry); | ||
512 | if (!mtime_valid) { | ||
513 | mtime = ncp_obtain_mtime(dentry); | ||
514 | mtime_valid = 1; | ||
515 | } | ||
516 | ctl.head.mtime = mtime; | ||
517 | ctl.head.time = jiffies; | ||
518 | ctl.head.eof = 0; | ||
519 | ctl.fpos = 2; | ||
520 | ctl.ofs = 0; | ||
521 | ctl.idx = NCP_DIRCACHE_START; | ||
522 | ctl.filled = 0; | ||
523 | ctl.valid = 1; | ||
524 | read_really: | ||
525 | if (ncp_is_server_root(inode)) { | ||
526 | ncp_read_volume_list(filp, dirent, filldir, &ctl); | ||
527 | } else { | ||
528 | ncp_do_readdir(filp, dirent, filldir, &ctl); | ||
529 | } | ||
530 | ctl.head.end = ctl.fpos - 1; | ||
531 | ctl.head.eof = ctl.valid; | ||
532 | finished: | ||
533 | if (page) { | ||
534 | cache->head = ctl.head; | ||
535 | kunmap(page); | ||
536 | SetPageUptodate(page); | ||
537 | unlock_page(page); | ||
538 | page_cache_release(page); | ||
539 | } | ||
540 | if (ctl.page) { | ||
541 | kunmap(ctl.page); | ||
542 | SetPageUptodate(ctl.page); | ||
543 | unlock_page(ctl.page); | ||
544 | page_cache_release(ctl.page); | ||
545 | } | ||
546 | out: | ||
547 | unlock_kernel(); | ||
548 | return result; | ||
549 | } | ||
550 | |||
551 | static int | ||
552 | ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir, | ||
553 | struct ncp_cache_control *ctrl, struct ncp_entry_info *entry) | ||
554 | { | ||
555 | struct dentry *newdent, *dentry = filp->f_dentry; | ||
556 | struct inode *newino, *inode = dentry->d_inode; | ||
557 | struct ncp_cache_control ctl = *ctrl; | ||
558 | struct qstr qname; | ||
559 | int valid = 0; | ||
560 | int hashed = 0; | ||
561 | ino_t ino = 0; | ||
562 | __u8 __name[NCP_MAXPATHLEN + 1]; | ||
563 | |||
564 | qname.len = sizeof(__name); | ||
565 | if (ncp_vol2io(NCP_SERVER(inode), __name, &qname.len, | ||
566 | entry->i.entryName, entry->i.nameLen, | ||
567 | !ncp_preserve_entry_case(inode, entry->i.NSCreator))) | ||
568 | return 1; /* I'm not sure */ | ||
569 | |||
570 | qname.name = __name; | ||
571 | qname.hash = full_name_hash(qname.name, qname.len); | ||
572 | |||
573 | if (dentry->d_op && dentry->d_op->d_hash) | ||
574 | if (dentry->d_op->d_hash(dentry, &qname) != 0) | ||
575 | goto end_advance; | ||
576 | |||
577 | newdent = d_lookup(dentry, &qname); | ||
578 | |||
579 | if (!newdent) { | ||
580 | newdent = d_alloc(dentry, &qname); | ||
581 | if (!newdent) | ||
582 | goto end_advance; | ||
583 | } else { | ||
584 | hashed = 1; | ||
585 | memcpy((char *) newdent->d_name.name, qname.name, | ||
586 | newdent->d_name.len); | ||
587 | } | ||
588 | |||
589 | if (!newdent->d_inode) { | ||
590 | entry->opened = 0; | ||
591 | entry->ino = iunique(inode->i_sb, 2); | ||
592 | newino = ncp_iget(inode->i_sb, entry); | ||
593 | if (newino) { | ||
594 | newdent->d_op = &ncp_dentry_operations; | ||
595 | d_instantiate(newdent, newino); | ||
596 | if (!hashed) | ||
597 | d_rehash(newdent); | ||
598 | } | ||
599 | } else | ||
600 | ncp_update_inode2(newdent->d_inode, entry); | ||
601 | |||
602 | if (newdent->d_inode) { | ||
603 | ino = newdent->d_inode->i_ino; | ||
604 | newdent->d_fsdata = (void *) ctl.fpos; | ||
605 | ncp_new_dentry(newdent); | ||
606 | } | ||
607 | |||
608 | if (ctl.idx >= NCP_DIRCACHE_SIZE) { | ||
609 | if (ctl.page) { | ||
610 | kunmap(ctl.page); | ||
611 | SetPageUptodate(ctl.page); | ||
612 | unlock_page(ctl.page); | ||
613 | page_cache_release(ctl.page); | ||
614 | } | ||
615 | ctl.cache = NULL; | ||
616 | ctl.idx -= NCP_DIRCACHE_SIZE; | ||
617 | ctl.ofs += 1; | ||
618 | ctl.page = grab_cache_page(&inode->i_data, ctl.ofs); | ||
619 | if (ctl.page) | ||
620 | ctl.cache = kmap(ctl.page); | ||
621 | } | ||
622 | if (ctl.cache) { | ||
623 | ctl.cache->dentry[ctl.idx] = newdent; | ||
624 | valid = 1; | ||
625 | } | ||
626 | dput(newdent); | ||
627 | end_advance: | ||
628 | if (!valid) | ||
629 | ctl.valid = 0; | ||
630 | if (!ctl.filled && (ctl.fpos == filp->f_pos)) { | ||
631 | if (!ino) | ||
632 | ino = find_inode_number(dentry, &qname); | ||
633 | if (!ino) | ||
634 | ino = iunique(inode->i_sb, 2); | ||
635 | ctl.filled = filldir(dirent, qname.name, qname.len, | ||
636 | filp->f_pos, ino, DT_UNKNOWN); | ||
637 | if (!ctl.filled) | ||
638 | filp->f_pos += 1; | ||
639 | } | ||
640 | ctl.fpos += 1; | ||
641 | ctl.idx += 1; | ||
642 | *ctrl = ctl; | ||
643 | return (ctl.valid || !ctl.filled); | ||
644 | } | ||
645 | |||
646 | static void | ||
647 | ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir, | ||
648 | struct ncp_cache_control *ctl) | ||
649 | { | ||
650 | struct dentry *dentry = filp->f_dentry; | ||
651 | struct inode *inode = dentry->d_inode; | ||
652 | struct ncp_server *server = NCP_SERVER(inode); | ||
653 | struct ncp_volume_info info; | ||
654 | struct ncp_entry_info entry; | ||
655 | int i; | ||
656 | |||
657 | DPRINTK("ncp_read_volume_list: pos=%ld\n", | ||
658 | (unsigned long) filp->f_pos); | ||
659 | |||
660 | for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) { | ||
661 | |||
662 | if (ncp_get_volume_info_with_number(server, i, &info) != 0) | ||
663 | return; | ||
664 | if (!strlen(info.volume_name)) | ||
665 | continue; | ||
666 | |||
667 | DPRINTK("ncp_read_volume_list: found vol: %s\n", | ||
668 | info.volume_name); | ||
669 | |||
670 | if (ncp_lookup_volume(server, info.volume_name, | ||
671 | &entry.i)) { | ||
672 | DPRINTK("ncpfs: could not lookup vol %s\n", | ||
673 | info.volume_name); | ||
674 | continue; | ||
675 | } | ||
676 | entry.volume = entry.i.volNumber; | ||
677 | if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry)) | ||
678 | return; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | static void | ||
683 | ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir, | ||
684 | struct ncp_cache_control *ctl) | ||
685 | { | ||
686 | struct dentry *dentry = filp->f_dentry; | ||
687 | struct inode *dir = dentry->d_inode; | ||
688 | struct ncp_server *server = NCP_SERVER(dir); | ||
689 | struct nw_search_sequence seq; | ||
690 | struct ncp_entry_info entry; | ||
691 | int err; | ||
692 | void* buf; | ||
693 | int more; | ||
694 | size_t bufsize; | ||
695 | |||
696 | DPRINTK("ncp_do_readdir: %s/%s, fpos=%ld\n", | ||
697 | dentry->d_parent->d_name.name, dentry->d_name.name, | ||
698 | (unsigned long) filp->f_pos); | ||
699 | PPRINTK("ncp_do_readdir: init %s, volnum=%d, dirent=%u\n", | ||
700 | dentry->d_name.name, NCP_FINFO(dir)->volNumber, | ||
701 | NCP_FINFO(dir)->dirEntNum); | ||
702 | |||
703 | err = ncp_initialize_search(server, dir, &seq); | ||
704 | if (err) { | ||
705 | DPRINTK("ncp_do_readdir: init failed, err=%d\n", err); | ||
706 | return; | ||
707 | } | ||
708 | #ifdef USE_OLD_SLOW_DIRECTORY_LISTING | ||
709 | for (;;) { | ||
710 | err = ncp_search_for_file_or_subdir(server, &seq, &entry.i); | ||
711 | if (err) { | ||
712 | DPRINTK("ncp_do_readdir: search failed, err=%d\n", err); | ||
713 | break; | ||
714 | } | ||
715 | entry.volume = entry.i.volNumber; | ||
716 | if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry)) | ||
717 | break; | ||
718 | } | ||
719 | #else | ||
720 | /* We MUST NOT use server->buffer_size handshaked with server if we are | ||
721 | using UDP, as for UDP server uses max. buffer size determined by | ||
722 | MTU, and for TCP server uses hardwired value 65KB (== 66560 bytes). | ||
723 | So we use 128KB, just to be sure, as there is no way how to know | ||
724 | this value in advance. */ | ||
725 | bufsize = 131072; | ||
726 | buf = vmalloc(bufsize); | ||
727 | if (!buf) | ||
728 | return; | ||
729 | do { | ||
730 | int cnt; | ||
731 | char* rpl; | ||
732 | size_t rpls; | ||
733 | |||
734 | err = ncp_search_for_fileset(server, &seq, &more, &cnt, buf, bufsize, &rpl, &rpls); | ||
735 | if (err) /* Error */ | ||
736 | break; | ||
737 | if (!cnt) /* prevent endless loop */ | ||
738 | break; | ||
739 | while (cnt--) { | ||
740 | size_t onerpl; | ||
741 | |||
742 | if (rpls < offsetof(struct nw_info_struct, entryName)) | ||
743 | break; /* short packet */ | ||
744 | ncp_extract_file_info(rpl, &entry.i); | ||
745 | onerpl = offsetof(struct nw_info_struct, entryName) + entry.i.nameLen; | ||
746 | if (rpls < onerpl) | ||
747 | break; /* short packet */ | ||
748 | (void)ncp_obtain_nfs_info(server, &entry.i); | ||
749 | rpl += onerpl; | ||
750 | rpls -= onerpl; | ||
751 | entry.volume = entry.i.volNumber; | ||
752 | if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry)) | ||
753 | break; | ||
754 | } | ||
755 | } while (more); | ||
756 | vfree(buf); | ||
757 | #endif | ||
758 | return; | ||
759 | } | ||
760 | |||
761 | int ncp_conn_logged_in(struct super_block *sb) | ||
762 | { | ||
763 | struct ncp_server* server = NCP_SBP(sb); | ||
764 | int result; | ||
765 | |||
766 | if (ncp_single_volume(server)) { | ||
767 | int len; | ||
768 | struct dentry* dent; | ||
769 | __u32 volNumber; | ||
770 | __le32 dirEntNum; | ||
771 | __le32 DosDirNum; | ||
772 | __u8 __name[NCP_MAXPATHLEN + 1]; | ||
773 | |||
774 | len = sizeof(__name); | ||
775 | result = ncp_io2vol(server, __name, &len, server->m.mounted_vol, | ||
776 | strlen(server->m.mounted_vol), 1); | ||
777 | if (result) | ||
778 | goto out; | ||
779 | result = -ENOENT; | ||
780 | if (ncp_get_volume_root(server, __name, &volNumber, &dirEntNum, &DosDirNum)) { | ||
781 | PPRINTK("ncp_conn_logged_in: %s not found\n", | ||
782 | server->m.mounted_vol); | ||
783 | goto out; | ||
784 | } | ||
785 | dent = sb->s_root; | ||
786 | if (dent) { | ||
787 | struct inode* ino = dent->d_inode; | ||
788 | if (ino) { | ||
789 | NCP_FINFO(ino)->volNumber = volNumber; | ||
790 | NCP_FINFO(ino)->dirEntNum = dirEntNum; | ||
791 | NCP_FINFO(ino)->DosDirNum = DosDirNum; | ||
792 | } else { | ||
793 | DPRINTK("ncpfs: sb->s_root->d_inode == NULL!\n"); | ||
794 | } | ||
795 | } else { | ||
796 | DPRINTK("ncpfs: sb->s_root == NULL!\n"); | ||
797 | } | ||
798 | } | ||
799 | result = 0; | ||
800 | |||
801 | out: | ||
802 | return result; | ||
803 | } | ||
804 | |||
805 | static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | ||
806 | { | ||
807 | struct ncp_server *server = NCP_SERVER(dir); | ||
808 | struct inode *inode = NULL; | ||
809 | struct ncp_entry_info finfo; | ||
810 | int error, res, len; | ||
811 | __u8 __name[NCP_MAXPATHLEN + 1]; | ||
812 | |||
813 | lock_kernel(); | ||
814 | error = -EIO; | ||
815 | if (!ncp_conn_valid(server)) | ||
816 | goto finished; | ||
817 | |||
818 | PPRINTK("ncp_lookup: server lookup for %s/%s\n", | ||
819 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
820 | |||
821 | len = sizeof(__name); | ||
822 | if (ncp_is_server_root(dir)) { | ||
823 | res = ncp_io2vol(server, __name, &len, dentry->d_name.name, | ||
824 | dentry->d_name.len, 1); | ||
825 | if (!res) | ||
826 | res = ncp_lookup_volume(server, __name, &(finfo.i)); | ||
827 | } else { | ||
828 | res = ncp_io2vol(server, __name, &len, dentry->d_name.name, | ||
829 | dentry->d_name.len, !ncp_preserve_case(dir)); | ||
830 | if (!res) | ||
831 | res = ncp_obtain_info(server, dir, __name, &(finfo.i)); | ||
832 | } | ||
833 | PPRINTK("ncp_lookup: looked for %s/%s, res=%d\n", | ||
834 | dentry->d_parent->d_name.name, __name, res); | ||
835 | /* | ||
836 | * If we didn't find an entry, make a negative dentry. | ||
837 | */ | ||
838 | if (res) | ||
839 | goto add_entry; | ||
840 | |||
841 | /* | ||
842 | * Create an inode for the entry. | ||
843 | */ | ||
844 | finfo.opened = 0; | ||
845 | finfo.ino = iunique(dir->i_sb, 2); | ||
846 | finfo.volume = finfo.i.volNumber; | ||
847 | error = -EACCES; | ||
848 | inode = ncp_iget(dir->i_sb, &finfo); | ||
849 | |||
850 | if (inode) { | ||
851 | ncp_new_dentry(dentry); | ||
852 | add_entry: | ||
853 | dentry->d_op = &ncp_dentry_operations; | ||
854 | d_add(dentry, inode); | ||
855 | error = 0; | ||
856 | } | ||
857 | |||
858 | finished: | ||
859 | PPRINTK("ncp_lookup: result=%d\n", error); | ||
860 | unlock_kernel(); | ||
861 | return ERR_PTR(error); | ||
862 | } | ||
863 | |||
864 | /* | ||
865 | * This code is common to create, mkdir, and mknod. | ||
866 | */ | ||
867 | static int ncp_instantiate(struct inode *dir, struct dentry *dentry, | ||
868 | struct ncp_entry_info *finfo) | ||
869 | { | ||
870 | struct inode *inode; | ||
871 | int error = -EINVAL; | ||
872 | |||
873 | finfo->ino = iunique(dir->i_sb, 2); | ||
874 | inode = ncp_iget(dir->i_sb, finfo); | ||
875 | if (!inode) | ||
876 | goto out_close; | ||
877 | d_instantiate(dentry,inode); | ||
878 | error = 0; | ||
879 | out: | ||
880 | return error; | ||
881 | |||
882 | out_close: | ||
883 | PPRINTK("ncp_instantiate: %s/%s failed, closing file\n", | ||
884 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
885 | ncp_close_file(NCP_SERVER(dir), finfo->file_handle); | ||
886 | goto out; | ||
887 | } | ||
888 | |||
889 | int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode, | ||
890 | dev_t rdev, __le32 attributes) | ||
891 | { | ||
892 | struct ncp_server *server = NCP_SERVER(dir); | ||
893 | struct ncp_entry_info finfo; | ||
894 | int error, result, len; | ||
895 | int opmode; | ||
896 | __u8 __name[NCP_MAXPATHLEN + 1]; | ||
897 | |||
898 | PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n", | ||
899 | dentry->d_parent->d_name.name, dentry->d_name.name, mode); | ||
900 | |||
901 | error = -EIO; | ||
902 | lock_kernel(); | ||
903 | if (!ncp_conn_valid(server)) | ||
904 | goto out; | ||
905 | |||
906 | ncp_age_dentry(server, dentry); | ||
907 | len = sizeof(__name); | ||
908 | error = ncp_io2vol(server, __name, &len, dentry->d_name.name, | ||
909 | dentry->d_name.len, !ncp_preserve_case(dir)); | ||
910 | if (error) | ||
911 | goto out; | ||
912 | |||
913 | error = -EACCES; | ||
914 | |||
915 | if (S_ISREG(mode) && | ||
916 | (server->m.flags & NCP_MOUNT_EXTRAS) && | ||
917 | (mode & S_IXUGO)) | ||
918 | attributes |= aSYSTEM | aSHARED; | ||
919 | |||
920 | result = ncp_open_create_file_or_subdir(server, dir, __name, | ||
921 | OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, | ||
922 | attributes, AR_READ | AR_WRITE, &finfo); | ||
923 | opmode = O_RDWR; | ||
924 | if (result) { | ||
925 | result = ncp_open_create_file_or_subdir(server, dir, __name, | ||
926 | OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, | ||
927 | attributes, AR_WRITE, &finfo); | ||
928 | if (result) { | ||
929 | if (result == 0x87) | ||
930 | error = -ENAMETOOLONG; | ||
931 | DPRINTK("ncp_create: %s/%s failed\n", | ||
932 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
933 | goto out; | ||
934 | } | ||
935 | opmode = O_WRONLY; | ||
936 | } | ||
937 | finfo.access = opmode; | ||
938 | if (ncp_is_nfs_extras(server, finfo.volume)) { | ||
939 | finfo.i.nfs.mode = mode; | ||
940 | finfo.i.nfs.rdev = new_encode_dev(rdev); | ||
941 | if (ncp_modify_nfs_info(server, finfo.volume, | ||
942 | finfo.i.dirEntNum, | ||
943 | mode, new_encode_dev(rdev)) != 0) | ||
944 | goto out; | ||
945 | } | ||
946 | |||
947 | error = ncp_instantiate(dir, dentry, &finfo); | ||
948 | out: | ||
949 | unlock_kernel(); | ||
950 | return error; | ||
951 | } | ||
952 | |||
953 | static int ncp_create(struct inode *dir, struct dentry *dentry, int mode, | ||
954 | struct nameidata *nd) | ||
955 | { | ||
956 | return ncp_create_new(dir, dentry, mode, 0, 0); | ||
957 | } | ||
958 | |||
959 | static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
960 | { | ||
961 | struct ncp_entry_info finfo; | ||
962 | struct ncp_server *server = NCP_SERVER(dir); | ||
963 | int error, len; | ||
964 | __u8 __name[NCP_MAXPATHLEN + 1]; | ||
965 | |||
966 | DPRINTK("ncp_mkdir: making %s/%s\n", | ||
967 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
968 | |||
969 | error = -EIO; | ||
970 | lock_kernel(); | ||
971 | if (!ncp_conn_valid(server)) | ||
972 | goto out; | ||
973 | |||
974 | ncp_age_dentry(server, dentry); | ||
975 | len = sizeof(__name); | ||
976 | error = ncp_io2vol(server, __name, &len, dentry->d_name.name, | ||
977 | dentry->d_name.len, !ncp_preserve_case(dir)); | ||
978 | if (error) | ||
979 | goto out; | ||
980 | |||
981 | error = -EACCES; | ||
982 | if (ncp_open_create_file_or_subdir(server, dir, __name, | ||
983 | OC_MODE_CREATE, aDIR, | ||
984 | cpu_to_le16(0xffff), | ||
985 | &finfo) == 0) | ||
986 | { | ||
987 | if (ncp_is_nfs_extras(server, finfo.volume)) { | ||
988 | mode |= S_IFDIR; | ||
989 | finfo.i.nfs.mode = mode; | ||
990 | if (ncp_modify_nfs_info(server, | ||
991 | finfo.volume, | ||
992 | finfo.i.dirEntNum, | ||
993 | mode, 0) != 0) | ||
994 | goto out; | ||
995 | } | ||
996 | error = ncp_instantiate(dir, dentry, &finfo); | ||
997 | } | ||
998 | out: | ||
999 | unlock_kernel(); | ||
1000 | return error; | ||
1001 | } | ||
1002 | |||
1003 | static int ncp_rmdir(struct inode *dir, struct dentry *dentry) | ||
1004 | { | ||
1005 | struct ncp_server *server = NCP_SERVER(dir); | ||
1006 | int error, result, len; | ||
1007 | __u8 __name[NCP_MAXPATHLEN + 1]; | ||
1008 | |||
1009 | DPRINTK("ncp_rmdir: removing %s/%s\n", | ||
1010 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
1011 | |||
1012 | error = -EIO; | ||
1013 | lock_kernel(); | ||
1014 | if (!ncp_conn_valid(server)) | ||
1015 | goto out; | ||
1016 | |||
1017 | error = -EBUSY; | ||
1018 | if (!d_unhashed(dentry)) | ||
1019 | goto out; | ||
1020 | |||
1021 | len = sizeof(__name); | ||
1022 | error = ncp_io2vol(server, __name, &len, dentry->d_name.name, | ||
1023 | dentry->d_name.len, !ncp_preserve_case(dir)); | ||
1024 | if (error) | ||
1025 | goto out; | ||
1026 | |||
1027 | result = ncp_del_file_or_subdir(server, dir, __name); | ||
1028 | switch (result) { | ||
1029 | case 0x00: | ||
1030 | error = 0; | ||
1031 | break; | ||
1032 | case 0x85: /* unauthorized to delete file */ | ||
1033 | case 0x8A: /* unauthorized to delete file */ | ||
1034 | error = -EACCES; | ||
1035 | break; | ||
1036 | case 0x8F: | ||
1037 | case 0x90: /* read only */ | ||
1038 | error = -EPERM; | ||
1039 | break; | ||
1040 | case 0x9F: /* in use by another client */ | ||
1041 | error = -EBUSY; | ||
1042 | break; | ||
1043 | case 0xA0: /* directory not empty */ | ||
1044 | error = -ENOTEMPTY; | ||
1045 | break; | ||
1046 | case 0xFF: /* someone deleted file */ | ||
1047 | error = -ENOENT; | ||
1048 | break; | ||
1049 | default: | ||
1050 | error = -EACCES; | ||
1051 | break; | ||
1052 | } | ||
1053 | out: | ||
1054 | unlock_kernel(); | ||
1055 | return error; | ||
1056 | } | ||
1057 | |||
1058 | static int ncp_unlink(struct inode *dir, struct dentry *dentry) | ||
1059 | { | ||
1060 | struct inode *inode = dentry->d_inode; | ||
1061 | struct ncp_server *server; | ||
1062 | int error; | ||
1063 | |||
1064 | lock_kernel(); | ||
1065 | server = NCP_SERVER(dir); | ||
1066 | DPRINTK("ncp_unlink: unlinking %s/%s\n", | ||
1067 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
1068 | |||
1069 | error = -EIO; | ||
1070 | if (!ncp_conn_valid(server)) | ||
1071 | goto out; | ||
1072 | |||
1073 | /* | ||
1074 | * Check whether to close the file ... | ||
1075 | */ | ||
1076 | if (inode) { | ||
1077 | PPRINTK("ncp_unlink: closing file\n"); | ||
1078 | ncp_make_closed(inode); | ||
1079 | } | ||
1080 | |||
1081 | error = ncp_del_file_or_subdir2(server, dentry); | ||
1082 | #ifdef CONFIG_NCPFS_STRONG | ||
1083 | /* 9C is Invalid path.. It should be 8F, 90 - read only, but | ||
1084 | it is not :-( */ | ||
1085 | if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */ | ||
1086 | error = ncp_force_unlink(dir, dentry); | ||
1087 | } | ||
1088 | #endif | ||
1089 | switch (error) { | ||
1090 | case 0x00: | ||
1091 | DPRINTK("ncp: removed %s/%s\n", | ||
1092 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
1093 | break; | ||
1094 | case 0x85: | ||
1095 | case 0x8A: | ||
1096 | error = -EACCES; | ||
1097 | break; | ||
1098 | case 0x8D: /* some files in use */ | ||
1099 | case 0x8E: /* all files in use */ | ||
1100 | error = -EBUSY; | ||
1101 | break; | ||
1102 | case 0x8F: /* some read only */ | ||
1103 | case 0x90: /* all read only */ | ||
1104 | case 0x9C: /* !!! returned when in-use or read-only by NW4 */ | ||
1105 | error = -EPERM; | ||
1106 | break; | ||
1107 | case 0xFF: | ||
1108 | error = -ENOENT; | ||
1109 | break; | ||
1110 | default: | ||
1111 | error = -EACCES; | ||
1112 | break; | ||
1113 | } | ||
1114 | |||
1115 | out: | ||
1116 | unlock_kernel(); | ||
1117 | return error; | ||
1118 | } | ||
1119 | |||
1120 | static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
1121 | struct inode *new_dir, struct dentry *new_dentry) | ||
1122 | { | ||
1123 | struct ncp_server *server = NCP_SERVER(old_dir); | ||
1124 | int error; | ||
1125 | int old_len, new_len; | ||
1126 | __u8 __old_name[NCP_MAXPATHLEN + 1], __new_name[NCP_MAXPATHLEN + 1]; | ||
1127 | |||
1128 | DPRINTK("ncp_rename: %s/%s to %s/%s\n", | ||
1129 | old_dentry->d_parent->d_name.name, old_dentry->d_name.name, | ||
1130 | new_dentry->d_parent->d_name.name, new_dentry->d_name.name); | ||
1131 | |||
1132 | error = -EIO; | ||
1133 | lock_kernel(); | ||
1134 | if (!ncp_conn_valid(server)) | ||
1135 | goto out; | ||
1136 | |||
1137 | ncp_age_dentry(server, old_dentry); | ||
1138 | ncp_age_dentry(server, new_dentry); | ||
1139 | |||
1140 | old_len = sizeof(__old_name); | ||
1141 | error = ncp_io2vol(server, __old_name, &old_len, | ||
1142 | old_dentry->d_name.name, old_dentry->d_name.len, | ||
1143 | !ncp_preserve_case(old_dir)); | ||
1144 | if (error) | ||
1145 | goto out; | ||
1146 | |||
1147 | new_len = sizeof(__new_name); | ||
1148 | error = ncp_io2vol(server, __new_name, &new_len, | ||
1149 | new_dentry->d_name.name, new_dentry->d_name.len, | ||
1150 | !ncp_preserve_case(new_dir)); | ||
1151 | if (error) | ||
1152 | goto out; | ||
1153 | |||
1154 | error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name, | ||
1155 | new_dir, __new_name); | ||
1156 | #ifdef CONFIG_NCPFS_STRONG | ||
1157 | if ((error == 0x90 || error == 0x8B || error == -EACCES) && | ||
1158 | server->m.flags & NCP_MOUNT_STRONG) { /* RO */ | ||
1159 | error = ncp_force_rename(old_dir, old_dentry, __old_name, | ||
1160 | new_dir, new_dentry, __new_name); | ||
1161 | } | ||
1162 | #endif | ||
1163 | switch (error) { | ||
1164 | case 0x00: | ||
1165 | DPRINTK("ncp renamed %s -> %s.\n", | ||
1166 | old_dentry->d_name.name,new_dentry->d_name.name); | ||
1167 | break; | ||
1168 | case 0x9E: | ||
1169 | error = -ENAMETOOLONG; | ||
1170 | break; | ||
1171 | case 0xFF: | ||
1172 | error = -ENOENT; | ||
1173 | break; | ||
1174 | default: | ||
1175 | error = -EACCES; | ||
1176 | break; | ||
1177 | } | ||
1178 | out: | ||
1179 | unlock_kernel(); | ||
1180 | return error; | ||
1181 | } | ||
1182 | |||
1183 | static int ncp_mknod(struct inode * dir, struct dentry *dentry, | ||
1184 | int mode, dev_t rdev) | ||
1185 | { | ||
1186 | if (!new_valid_dev(rdev)) | ||
1187 | return -EINVAL; | ||
1188 | if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) { | ||
1189 | DPRINTK(KERN_DEBUG "ncp_mknod: mode = 0%o\n", mode); | ||
1190 | return ncp_create_new(dir, dentry, mode, rdev, 0); | ||
1191 | } | ||
1192 | return -EPERM; /* Strange, but true */ | ||
1193 | } | ||
1194 | |||
1195 | /* The following routines are taken directly from msdos-fs */ | ||
1196 | |||
1197 | /* Linear day numbers of the respective 1sts in non-leap years. */ | ||
1198 | |||
1199 | static int day_n[] = | ||
1200 | {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; | ||
1201 | /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ | ||
1202 | |||
1203 | |||
1204 | extern struct timezone sys_tz; | ||
1205 | |||
1206 | static int utc2local(int time) | ||
1207 | { | ||
1208 | return time - sys_tz.tz_minuteswest * 60; | ||
1209 | } | ||
1210 | |||
1211 | static int local2utc(int time) | ||
1212 | { | ||
1213 | return time + sys_tz.tz_minuteswest * 60; | ||
1214 | } | ||
1215 | |||
1216 | /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ | ||
1217 | int | ||
1218 | ncp_date_dos2unix(__le16 t, __le16 d) | ||
1219 | { | ||
1220 | unsigned short time = le16_to_cpu(t), date = le16_to_cpu(d); | ||
1221 | int month, year, secs; | ||
1222 | |||
1223 | /* first subtract and mask after that... Otherwise, if | ||
1224 | date == 0, bad things happen */ | ||
1225 | month = ((date >> 5) - 1) & 15; | ||
1226 | year = date >> 9; | ||
1227 | secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + | ||
1228 | 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + | ||
1229 | year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); | ||
1230 | /* days since 1.1.70 plus 80's leap day */ | ||
1231 | return local2utc(secs); | ||
1232 | } | ||
1233 | |||
1234 | |||
1235 | /* Convert linear UNIX date to a MS-DOS time/date pair. */ | ||
1236 | void | ||
1237 | ncp_date_unix2dos(int unix_date, __le16 *time, __le16 *date) | ||
1238 | { | ||
1239 | int day, year, nl_day, month; | ||
1240 | |||
1241 | unix_date = utc2local(unix_date); | ||
1242 | *time = cpu_to_le16( | ||
1243 | (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) + | ||
1244 | (((unix_date / 3600) % 24) << 11)); | ||
1245 | day = unix_date / 86400 - 3652; | ||
1246 | year = day / 365; | ||
1247 | if ((year + 3) / 4 + 365 * year > day) | ||
1248 | year--; | ||
1249 | day -= (year + 3) / 4 + 365 * year; | ||
1250 | if (day == 59 && !(year & 3)) { | ||
1251 | nl_day = day; | ||
1252 | month = 2; | ||
1253 | } else { | ||
1254 | nl_day = (year & 3) || day <= 59 ? day : day - 1; | ||
1255 | for (month = 0; month < 12; month++) | ||
1256 | if (day_n[month] > nl_day) | ||
1257 | break; | ||
1258 | } | ||
1259 | *date = cpu_to_le16(nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9)); | ||
1260 | } | ||
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c new file mode 100644 index 000000000000..4947d9b11fc1 --- /dev/null +++ b/fs/ncpfs/file.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | * file.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 by Volker Lendecke | ||
5 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include <asm/uaccess.h> | ||
10 | #include <asm/system.h> | ||
11 | |||
12 | #include <linux/time.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/fcntl.h> | ||
16 | #include <linux/stat.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | |||
22 | #include <linux/ncp_fs.h> | ||
23 | #include "ncplib_kernel.h" | ||
24 | |||
25 | static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync) | ||
26 | { | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | /* | ||
31 | * Open a file with the specified read/write mode. | ||
32 | */ | ||
33 | int ncp_make_open(struct inode *inode, int right) | ||
34 | { | ||
35 | int error; | ||
36 | int access; | ||
37 | |||
38 | error = -EINVAL; | ||
39 | if (!inode) { | ||
40 | printk(KERN_ERR "ncp_make_open: got NULL inode\n"); | ||
41 | goto out; | ||
42 | } | ||
43 | |||
44 | DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n", | ||
45 | atomic_read(&NCP_FINFO(inode)->opened), | ||
46 | NCP_FINFO(inode)->volNumber, | ||
47 | NCP_FINFO(inode)->dirEntNum); | ||
48 | error = -EACCES; | ||
49 | down(&NCP_FINFO(inode)->open_sem); | ||
50 | if (!atomic_read(&NCP_FINFO(inode)->opened)) { | ||
51 | struct ncp_entry_info finfo; | ||
52 | int result; | ||
53 | |||
54 | /* tries max. rights */ | ||
55 | finfo.access = O_RDWR; | ||
56 | result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), | ||
57 | inode, NULL, OC_MODE_OPEN, | ||
58 | 0, AR_READ | AR_WRITE, &finfo); | ||
59 | if (!result) | ||
60 | goto update; | ||
61 | /* RDWR did not succeeded, try readonly or writeonly as requested */ | ||
62 | switch (right) { | ||
63 | case O_RDONLY: | ||
64 | finfo.access = O_RDONLY; | ||
65 | result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), | ||
66 | inode, NULL, OC_MODE_OPEN, | ||
67 | 0, AR_READ, &finfo); | ||
68 | break; | ||
69 | case O_WRONLY: | ||
70 | finfo.access = O_WRONLY; | ||
71 | result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), | ||
72 | inode, NULL, OC_MODE_OPEN, | ||
73 | 0, AR_WRITE, &finfo); | ||
74 | break; | ||
75 | } | ||
76 | if (result) { | ||
77 | PPRINTK("ncp_make_open: failed, result=%d\n", result); | ||
78 | goto out_unlock; | ||
79 | } | ||
80 | /* | ||
81 | * Update the inode information. | ||
82 | */ | ||
83 | update: | ||
84 | ncp_update_inode(inode, &finfo); | ||
85 | atomic_set(&NCP_FINFO(inode)->opened, 1); | ||
86 | } | ||
87 | |||
88 | access = NCP_FINFO(inode)->access; | ||
89 | PPRINTK("ncp_make_open: file open, access=%x\n", access); | ||
90 | if (access == right || access == O_RDWR) { | ||
91 | atomic_inc(&NCP_FINFO(inode)->opened); | ||
92 | error = 0; | ||
93 | } | ||
94 | |||
95 | out_unlock: | ||
96 | up(&NCP_FINFO(inode)->open_sem); | ||
97 | out: | ||
98 | return error; | ||
99 | } | ||
100 | |||
101 | static ssize_t | ||
102 | ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
103 | { | ||
104 | struct dentry *dentry = file->f_dentry; | ||
105 | struct inode *inode = dentry->d_inode; | ||
106 | size_t already_read = 0; | ||
107 | off_t pos; | ||
108 | size_t bufsize; | ||
109 | int error; | ||
110 | void* freepage; | ||
111 | size_t freelen; | ||
112 | |||
113 | DPRINTK("ncp_file_read: enter %s/%s\n", | ||
114 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
115 | |||
116 | if (!ncp_conn_valid(NCP_SERVER(inode))) | ||
117 | return -EIO; | ||
118 | |||
119 | pos = *ppos; | ||
120 | |||
121 | if ((ssize_t) count < 0) { | ||
122 | return -EINVAL; | ||
123 | } | ||
124 | if (!count) | ||
125 | return 0; | ||
126 | if (pos > inode->i_sb->s_maxbytes) | ||
127 | return 0; | ||
128 | if (pos + count > inode->i_sb->s_maxbytes) { | ||
129 | count = inode->i_sb->s_maxbytes - pos; | ||
130 | } | ||
131 | |||
132 | error = ncp_make_open(inode, O_RDONLY); | ||
133 | if (error) { | ||
134 | DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); | ||
135 | return error; | ||
136 | } | ||
137 | |||
138 | bufsize = NCP_SERVER(inode)->buffer_size; | ||
139 | |||
140 | error = -EIO; | ||
141 | freelen = ncp_read_bounce_size(bufsize); | ||
142 | freepage = vmalloc(freelen); | ||
143 | if (!freepage) | ||
144 | goto outrel; | ||
145 | error = 0; | ||
146 | /* First read in as much as possible for each bufsize. */ | ||
147 | while (already_read < count) { | ||
148 | int read_this_time; | ||
149 | size_t to_read = min_t(unsigned int, | ||
150 | bufsize - (pos % bufsize), | ||
151 | count - already_read); | ||
152 | |||
153 | error = ncp_read_bounce(NCP_SERVER(inode), | ||
154 | NCP_FINFO(inode)->file_handle, | ||
155 | pos, to_read, buf, &read_this_time, | ||
156 | freepage, freelen); | ||
157 | if (error) { | ||
158 | error = -EIO; /* NW errno -> Linux errno */ | ||
159 | break; | ||
160 | } | ||
161 | pos += read_this_time; | ||
162 | buf += read_this_time; | ||
163 | already_read += read_this_time; | ||
164 | |||
165 | if (read_this_time != to_read) { | ||
166 | break; | ||
167 | } | ||
168 | } | ||
169 | vfree(freepage); | ||
170 | |||
171 | *ppos = pos; | ||
172 | |||
173 | file_accessed(file); | ||
174 | |||
175 | DPRINTK("ncp_file_read: exit %s/%s\n", | ||
176 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
177 | outrel: | ||
178 | ncp_inode_close(inode); | ||
179 | return already_read ? already_read : error; | ||
180 | } | ||
181 | |||
182 | static ssize_t | ||
183 | ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||
184 | { | ||
185 | struct dentry *dentry = file->f_dentry; | ||
186 | struct inode *inode = dentry->d_inode; | ||
187 | size_t already_written = 0; | ||
188 | off_t pos; | ||
189 | size_t bufsize; | ||
190 | int errno; | ||
191 | void* bouncebuffer; | ||
192 | |||
193 | DPRINTK("ncp_file_write: enter %s/%s\n", | ||
194 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
195 | if (!ncp_conn_valid(NCP_SERVER(inode))) | ||
196 | return -EIO; | ||
197 | if ((ssize_t) count < 0) | ||
198 | return -EINVAL; | ||
199 | pos = *ppos; | ||
200 | if (file->f_flags & O_APPEND) { | ||
201 | pos = inode->i_size; | ||
202 | } | ||
203 | |||
204 | if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) { | ||
205 | if (pos >= MAX_NON_LFS) { | ||
206 | send_sig(SIGXFSZ, current, 0); | ||
207 | return -EFBIG; | ||
208 | } | ||
209 | if (count > MAX_NON_LFS - (u32)pos) { | ||
210 | count = MAX_NON_LFS - (u32)pos; | ||
211 | } | ||
212 | } | ||
213 | if (pos >= inode->i_sb->s_maxbytes) { | ||
214 | if (count || pos > inode->i_sb->s_maxbytes) { | ||
215 | send_sig(SIGXFSZ, current, 0); | ||
216 | return -EFBIG; | ||
217 | } | ||
218 | } | ||
219 | if (pos + count > inode->i_sb->s_maxbytes) { | ||
220 | count = inode->i_sb->s_maxbytes - pos; | ||
221 | } | ||
222 | |||
223 | if (!count) | ||
224 | return 0; | ||
225 | errno = ncp_make_open(inode, O_WRONLY); | ||
226 | if (errno) { | ||
227 | DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); | ||
228 | return errno; | ||
229 | } | ||
230 | bufsize = NCP_SERVER(inode)->buffer_size; | ||
231 | |||
232 | already_written = 0; | ||
233 | |||
234 | bouncebuffer = vmalloc(bufsize); | ||
235 | if (!bouncebuffer) { | ||
236 | errno = -EIO; /* -ENOMEM */ | ||
237 | goto outrel; | ||
238 | } | ||
239 | while (already_written < count) { | ||
240 | int written_this_time; | ||
241 | size_t to_write = min_t(unsigned int, | ||
242 | bufsize - (pos % bufsize), | ||
243 | count - already_written); | ||
244 | |||
245 | if (copy_from_user(bouncebuffer, buf, to_write)) { | ||
246 | errno = -EFAULT; | ||
247 | break; | ||
248 | } | ||
249 | if (ncp_write_kernel(NCP_SERVER(inode), | ||
250 | NCP_FINFO(inode)->file_handle, | ||
251 | pos, to_write, bouncebuffer, &written_this_time) != 0) { | ||
252 | errno = -EIO; | ||
253 | break; | ||
254 | } | ||
255 | pos += written_this_time; | ||
256 | buf += written_this_time; | ||
257 | already_written += written_this_time; | ||
258 | |||
259 | if (written_this_time != to_write) { | ||
260 | break; | ||
261 | } | ||
262 | } | ||
263 | vfree(bouncebuffer); | ||
264 | |||
265 | inode_update_time(inode, 1); | ||
266 | |||
267 | *ppos = pos; | ||
268 | |||
269 | if (pos > inode->i_size) { | ||
270 | inode->i_size = pos; | ||
271 | } | ||
272 | DPRINTK("ncp_file_write: exit %s/%s\n", | ||
273 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
274 | outrel: | ||
275 | ncp_inode_close(inode); | ||
276 | return already_written ? already_written : errno; | ||
277 | } | ||
278 | |||
279 | static int ncp_release(struct inode *inode, struct file *file) { | ||
280 | if (ncp_make_closed(inode)) { | ||
281 | DPRINTK("ncp_release: failed to close\n"); | ||
282 | } | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | struct file_operations ncp_file_operations = | ||
287 | { | ||
288 | .llseek = remote_llseek, | ||
289 | .read = ncp_file_read, | ||
290 | .write = ncp_file_write, | ||
291 | .ioctl = ncp_ioctl, | ||
292 | .mmap = ncp_mmap, | ||
293 | .release = ncp_release, | ||
294 | .fsync = ncp_fsync, | ||
295 | }; | ||
296 | |||
297 | struct inode_operations ncp_file_inode_operations = | ||
298 | { | ||
299 | .setattr = ncp_notify_change, | ||
300 | }; | ||
diff --git a/fs/ncpfs/getopt.c b/fs/ncpfs/getopt.c new file mode 100644 index 000000000000..335b003dddf9 --- /dev/null +++ b/fs/ncpfs/getopt.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * getopt.c | ||
3 | */ | ||
4 | |||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/string.h> | ||
7 | |||
8 | #include <asm/errno.h> | ||
9 | |||
10 | #include "getopt.h" | ||
11 | |||
12 | /** | ||
13 | * ncp_getopt - option parser | ||
14 | * @caller: name of the caller, for error messages | ||
15 | * @options: the options string | ||
16 | * @opts: an array of &struct option entries controlling parser operations | ||
17 | * @optopt: output; will contain the current option | ||
18 | * @optarg: output; will contain the value (if one exists) | ||
19 | * @flag: output; may be NULL; should point to a long for or'ing flags | ||
20 | * @value: output; may be NULL; will be overwritten with the integer value | ||
21 | * of the current argument. | ||
22 | * | ||
23 | * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). | ||
24 | * Returns opts->val if a matching entry in the 'opts' array is found, | ||
25 | * 0 when no more tokens are found, -1 if an error is encountered. | ||
26 | */ | ||
27 | int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts, | ||
28 | char **optopt, char **optarg, unsigned long *value) | ||
29 | { | ||
30 | char *token; | ||
31 | char *val; | ||
32 | |||
33 | do { | ||
34 | if ((token = strsep(options, ",")) == NULL) | ||
35 | return 0; | ||
36 | } while (*token == '\0'); | ||
37 | if (optopt) | ||
38 | *optopt = token; | ||
39 | |||
40 | if ((val = strchr (token, '=')) != NULL) { | ||
41 | *val++ = 0; | ||
42 | } | ||
43 | *optarg = val; | ||
44 | for (; opts->name; opts++) { | ||
45 | if (!strcmp(opts->name, token)) { | ||
46 | if (!val) { | ||
47 | if (opts->has_arg & OPT_NOPARAM) { | ||
48 | return opts->val; | ||
49 | } | ||
50 | printk(KERN_INFO "%s: the %s option requires an argument\n", | ||
51 | caller, token); | ||
52 | return -EINVAL; | ||
53 | } | ||
54 | if (opts->has_arg & OPT_INT) { | ||
55 | char* v; | ||
56 | |||
57 | *value = simple_strtoul(val, &v, 0); | ||
58 | if (!*v) { | ||
59 | return opts->val; | ||
60 | } | ||
61 | printk(KERN_INFO "%s: invalid numeric value in %s=%s\n", | ||
62 | caller, token, val); | ||
63 | return -EDOM; | ||
64 | } | ||
65 | if (opts->has_arg & OPT_STRING) { | ||
66 | return opts->val; | ||
67 | } | ||
68 | printk(KERN_INFO "%s: unexpected argument %s to the %s option\n", | ||
69 | caller, val, token); | ||
70 | return -EINVAL; | ||
71 | } | ||
72 | } | ||
73 | printk(KERN_INFO "%s: Unrecognized mount option %s\n", caller, token); | ||
74 | return -EOPNOTSUPP; | ||
75 | } | ||
diff --git a/fs/ncpfs/getopt.h b/fs/ncpfs/getopt.h new file mode 100644 index 000000000000..cccc007dcaf9 --- /dev/null +++ b/fs/ncpfs/getopt.h | |||
@@ -0,0 +1,16 @@ | |||
1 | #ifndef _LINUX_GETOPT_H | ||
2 | #define _LINUX_GETOPT_H | ||
3 | |||
4 | #define OPT_NOPARAM 1 | ||
5 | #define OPT_INT 2 | ||
6 | #define OPT_STRING 4 | ||
7 | struct ncp_option { | ||
8 | const char *name; | ||
9 | unsigned int has_arg; | ||
10 | int val; | ||
11 | }; | ||
12 | |||
13 | extern int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts, | ||
14 | char **optopt, char **optarg, unsigned long *value); | ||
15 | |||
16 | #endif /* _LINUX_GETOPT_H */ | ||
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c new file mode 100644 index 000000000000..44795d2f4b30 --- /dev/null +++ b/fs/ncpfs/inode.c | |||
@@ -0,0 +1,1012 @@ | |||
1 | /* | ||
2 | * inode.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 by Volker Lendecke | ||
5 | * Modified for big endian by J.F. Chadima and David S. Miller | ||
6 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
7 | * Modified 1998 Wolfram Pienkoss for NLS | ||
8 | * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/module.h> | ||
14 | |||
15 | #include <asm/system.h> | ||
16 | #include <asm/uaccess.h> | ||
17 | #include <asm/byteorder.h> | ||
18 | |||
19 | #include <linux/time.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/stat.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/file.h> | ||
26 | #include <linux/fcntl.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/vmalloc.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/smp_lock.h> | ||
31 | #include <linux/vfs.h> | ||
32 | |||
33 | #include <linux/ncp_fs.h> | ||
34 | |||
35 | #include <net/sock.h> | ||
36 | |||
37 | #include "ncplib_kernel.h" | ||
38 | #include "getopt.h" | ||
39 | |||
40 | static void ncp_delete_inode(struct inode *); | ||
41 | static void ncp_put_super(struct super_block *); | ||
42 | static int ncp_statfs(struct super_block *, struct kstatfs *); | ||
43 | |||
44 | static kmem_cache_t * ncp_inode_cachep; | ||
45 | |||
46 | static struct inode *ncp_alloc_inode(struct super_block *sb) | ||
47 | { | ||
48 | struct ncp_inode_info *ei; | ||
49 | ei = (struct ncp_inode_info *)kmem_cache_alloc(ncp_inode_cachep, SLAB_KERNEL); | ||
50 | if (!ei) | ||
51 | return NULL; | ||
52 | return &ei->vfs_inode; | ||
53 | } | ||
54 | |||
55 | static void ncp_destroy_inode(struct inode *inode) | ||
56 | { | ||
57 | kmem_cache_free(ncp_inode_cachep, NCP_FINFO(inode)); | ||
58 | } | ||
59 | |||
60 | static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) | ||
61 | { | ||
62 | struct ncp_inode_info *ei = (struct ncp_inode_info *) foo; | ||
63 | |||
64 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | ||
65 | SLAB_CTOR_CONSTRUCTOR) { | ||
66 | init_MUTEX(&ei->open_sem); | ||
67 | inode_init_once(&ei->vfs_inode); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static int init_inodecache(void) | ||
72 | { | ||
73 | ncp_inode_cachep = kmem_cache_create("ncp_inode_cache", | ||
74 | sizeof(struct ncp_inode_info), | ||
75 | 0, SLAB_RECLAIM_ACCOUNT, | ||
76 | init_once, NULL); | ||
77 | if (ncp_inode_cachep == NULL) | ||
78 | return -ENOMEM; | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static void destroy_inodecache(void) | ||
83 | { | ||
84 | if (kmem_cache_destroy(ncp_inode_cachep)) | ||
85 | printk(KERN_INFO "ncp_inode_cache: not all structures were freed\n"); | ||
86 | } | ||
87 | |||
88 | static int ncp_remount(struct super_block *sb, int *flags, char* data) | ||
89 | { | ||
90 | *flags |= MS_NODIRATIME; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static struct super_operations ncp_sops = | ||
95 | { | ||
96 | .alloc_inode = ncp_alloc_inode, | ||
97 | .destroy_inode = ncp_destroy_inode, | ||
98 | .drop_inode = generic_delete_inode, | ||
99 | .delete_inode = ncp_delete_inode, | ||
100 | .put_super = ncp_put_super, | ||
101 | .statfs = ncp_statfs, | ||
102 | .remount_fs = ncp_remount, | ||
103 | }; | ||
104 | |||
105 | extern struct dentry_operations ncp_root_dentry_operations; | ||
106 | #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) | ||
107 | extern struct address_space_operations ncp_symlink_aops; | ||
108 | extern int ncp_symlink(struct inode*, struct dentry*, const char*); | ||
109 | #endif | ||
110 | |||
111 | /* | ||
112 | * Fill in the ncpfs-specific information in the inode. | ||
113 | */ | ||
114 | static void ncp_update_dirent(struct inode *inode, struct ncp_entry_info *nwinfo) | ||
115 | { | ||
116 | NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum; | ||
117 | NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum; | ||
118 | NCP_FINFO(inode)->volNumber = nwinfo->volume; | ||
119 | } | ||
120 | |||
121 | void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo) | ||
122 | { | ||
123 | ncp_update_dirent(inode, nwinfo); | ||
124 | NCP_FINFO(inode)->nwattr = nwinfo->i.attributes; | ||
125 | NCP_FINFO(inode)->access = nwinfo->access; | ||
126 | memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle, | ||
127 | sizeof(nwinfo->file_handle)); | ||
128 | DPRINTK("ncp_update_inode: updated %s, volnum=%d, dirent=%u\n", | ||
129 | nwinfo->i.entryName, NCP_FINFO(inode)->volNumber, | ||
130 | NCP_FINFO(inode)->dirEntNum); | ||
131 | } | ||
132 | |||
133 | static void ncp_update_dates(struct inode *inode, struct nw_info_struct *nwi) | ||
134 | { | ||
135 | /* NFS namespace mode overrides others if it's set. */ | ||
136 | DPRINTK(KERN_DEBUG "ncp_update_dates_and_mode: (%s) nfs.mode=0%o\n", | ||
137 | nwi->entryName, nwi->nfs.mode); | ||
138 | if (nwi->nfs.mode) { | ||
139 | /* XXX Security? */ | ||
140 | inode->i_mode = nwi->nfs.mode; | ||
141 | } | ||
142 | |||
143 | inode->i_blocks = (inode->i_size + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT; | ||
144 | |||
145 | inode->i_mtime.tv_sec = ncp_date_dos2unix(nwi->modifyTime, nwi->modifyDate); | ||
146 | inode->i_ctime.tv_sec = ncp_date_dos2unix(nwi->creationTime, nwi->creationDate); | ||
147 | inode->i_atime.tv_sec = ncp_date_dos2unix(0, nwi->lastAccessDate); | ||
148 | inode->i_atime.tv_nsec = 0; | ||
149 | inode->i_mtime.tv_nsec = 0; | ||
150 | inode->i_ctime.tv_nsec = 0; | ||
151 | } | ||
152 | |||
153 | static void ncp_update_attrs(struct inode *inode, struct ncp_entry_info *nwinfo) | ||
154 | { | ||
155 | struct nw_info_struct *nwi = &nwinfo->i; | ||
156 | struct ncp_server *server = NCP_SERVER(inode); | ||
157 | |||
158 | if (nwi->attributes & aDIR) { | ||
159 | inode->i_mode = server->m.dir_mode; | ||
160 | /* for directories dataStreamSize seems to be some | ||
161 | Object ID ??? */ | ||
162 | inode->i_size = NCP_BLOCK_SIZE; | ||
163 | } else { | ||
164 | inode->i_mode = server->m.file_mode; | ||
165 | inode->i_size = le32_to_cpu(nwi->dataStreamSize); | ||
166 | #ifdef CONFIG_NCPFS_EXTRAS | ||
167 | if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) | ||
168 | && (nwi->attributes & aSHARED)) { | ||
169 | switch (nwi->attributes & (aHIDDEN|aSYSTEM)) { | ||
170 | case aHIDDEN: | ||
171 | if (server->m.flags & NCP_MOUNT_SYMLINKS) { | ||
172 | if (/* (inode->i_size >= NCP_MIN_SYMLINK_SIZE) | ||
173 | && */ (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) { | ||
174 | inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK; | ||
175 | NCP_FINFO(inode)->flags |= NCPI_KLUDGE_SYMLINK; | ||
176 | break; | ||
177 | } | ||
178 | } | ||
179 | /* FALLTHROUGH */ | ||
180 | case 0: | ||
181 | if (server->m.flags & NCP_MOUNT_EXTRAS) | ||
182 | inode->i_mode |= S_IRUGO; | ||
183 | break; | ||
184 | case aSYSTEM: | ||
185 | if (server->m.flags & NCP_MOUNT_EXTRAS) | ||
186 | inode->i_mode |= (inode->i_mode >> 2) & S_IXUGO; | ||
187 | break; | ||
188 | /* case aSYSTEM|aHIDDEN: */ | ||
189 | default: | ||
190 | /* reserved combination */ | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | #endif | ||
195 | } | ||
196 | if (nwi->attributes & aRONLY) inode->i_mode &= ~S_IWUGO; | ||
197 | } | ||
198 | |||
199 | void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo) | ||
200 | { | ||
201 | NCP_FINFO(inode)->flags = 0; | ||
202 | if (!atomic_read(&NCP_FINFO(inode)->opened)) { | ||
203 | NCP_FINFO(inode)->nwattr = nwinfo->i.attributes; | ||
204 | ncp_update_attrs(inode, nwinfo); | ||
205 | } | ||
206 | |||
207 | ncp_update_dates(inode, &nwinfo->i); | ||
208 | ncp_update_dirent(inode, nwinfo); | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Fill in the inode based on the ncp_entry_info structure. | ||
213 | */ | ||
214 | static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo) | ||
215 | { | ||
216 | struct ncp_server *server = NCP_SERVER(inode); | ||
217 | |||
218 | NCP_FINFO(inode)->flags = 0; | ||
219 | |||
220 | ncp_update_attrs(inode, nwinfo); | ||
221 | |||
222 | DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode); | ||
223 | |||
224 | inode->i_nlink = 1; | ||
225 | inode->i_uid = server->m.uid; | ||
226 | inode->i_gid = server->m.gid; | ||
227 | inode->i_blksize = NCP_BLOCK_SIZE; | ||
228 | |||
229 | ncp_update_dates(inode, &nwinfo->i); | ||
230 | ncp_update_inode(inode, nwinfo); | ||
231 | } | ||
232 | |||
233 | #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) | ||
234 | static struct inode_operations ncp_symlink_inode_operations = { | ||
235 | .readlink = generic_readlink, | ||
236 | .follow_link = page_follow_link_light, | ||
237 | .put_link = page_put_link, | ||
238 | .setattr = ncp_notify_change, | ||
239 | }; | ||
240 | #endif | ||
241 | |||
242 | /* | ||
243 | * Get a new inode. | ||
244 | */ | ||
245 | struct inode * | ||
246 | ncp_iget(struct super_block *sb, struct ncp_entry_info *info) | ||
247 | { | ||
248 | struct inode *inode; | ||
249 | |||
250 | if (info == NULL) { | ||
251 | printk(KERN_ERR "ncp_iget: info is NULL\n"); | ||
252 | return NULL; | ||
253 | } | ||
254 | |||
255 | inode = new_inode(sb); | ||
256 | if (inode) { | ||
257 | atomic_set(&NCP_FINFO(inode)->opened, info->opened); | ||
258 | |||
259 | inode->i_ino = info->ino; | ||
260 | ncp_set_attr(inode, info); | ||
261 | if (S_ISREG(inode->i_mode)) { | ||
262 | inode->i_op = &ncp_file_inode_operations; | ||
263 | inode->i_fop = &ncp_file_operations; | ||
264 | } else if (S_ISDIR(inode->i_mode)) { | ||
265 | inode->i_op = &ncp_dir_inode_operations; | ||
266 | inode->i_fop = &ncp_dir_operations; | ||
267 | #ifdef CONFIG_NCPFS_NFS_NS | ||
268 | } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { | ||
269 | init_special_inode(inode, inode->i_mode, | ||
270 | new_decode_dev(info->i.nfs.rdev)); | ||
271 | #endif | ||
272 | #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) | ||
273 | } else if (S_ISLNK(inode->i_mode)) { | ||
274 | inode->i_op = &ncp_symlink_inode_operations; | ||
275 | inode->i_data.a_ops = &ncp_symlink_aops; | ||
276 | #endif | ||
277 | } else { | ||
278 | make_bad_inode(inode); | ||
279 | } | ||
280 | insert_inode_hash(inode); | ||
281 | } else | ||
282 | printk(KERN_ERR "ncp_iget: iget failed!\n"); | ||
283 | return inode; | ||
284 | } | ||
285 | |||
286 | static void | ||
287 | ncp_delete_inode(struct inode *inode) | ||
288 | { | ||
289 | if (S_ISDIR(inode->i_mode)) { | ||
290 | DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino); | ||
291 | } | ||
292 | |||
293 | if (ncp_make_closed(inode) != 0) { | ||
294 | /* We can't do anything but complain. */ | ||
295 | printk(KERN_ERR "ncp_delete_inode: could not close\n"); | ||
296 | } | ||
297 | clear_inode(inode); | ||
298 | } | ||
299 | |||
300 | static void ncp_stop_tasks(struct ncp_server *server) { | ||
301 | struct sock* sk = server->ncp_sock->sk; | ||
302 | |||
303 | sk->sk_error_report = server->error_report; | ||
304 | sk->sk_data_ready = server->data_ready; | ||
305 | sk->sk_write_space = server->write_space; | ||
306 | del_timer_sync(&server->timeout_tm); | ||
307 | flush_scheduled_work(); | ||
308 | } | ||
309 | |||
310 | static const struct ncp_option ncp_opts[] = { | ||
311 | { "uid", OPT_INT, 'u' }, | ||
312 | { "gid", OPT_INT, 'g' }, | ||
313 | { "owner", OPT_INT, 'o' }, | ||
314 | { "mode", OPT_INT, 'm' }, | ||
315 | { "dirmode", OPT_INT, 'd' }, | ||
316 | { "timeout", OPT_INT, 't' }, | ||
317 | { "retry", OPT_INT, 'r' }, | ||
318 | { "flags", OPT_INT, 'f' }, | ||
319 | { "wdogpid", OPT_INT, 'w' }, | ||
320 | { "ncpfd", OPT_INT, 'n' }, | ||
321 | { "infofd", OPT_INT, 'i' }, /* v5 */ | ||
322 | { "version", OPT_INT, 'v' }, | ||
323 | { NULL, 0, 0 } }; | ||
324 | |||
325 | static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) { | ||
326 | int optval; | ||
327 | char *optarg; | ||
328 | unsigned long optint; | ||
329 | int version = 0; | ||
330 | |||
331 | data->flags = 0; | ||
332 | data->int_flags = 0; | ||
333 | data->mounted_uid = 0; | ||
334 | data->wdog_pid = -1; | ||
335 | data->ncp_fd = ~0; | ||
336 | data->time_out = 10; | ||
337 | data->retry_count = 20; | ||
338 | data->uid = 0; | ||
339 | data->gid = 0; | ||
340 | data->file_mode = 0600; | ||
341 | data->dir_mode = 0700; | ||
342 | data->info_fd = -1; | ||
343 | data->mounted_vol[0] = 0; | ||
344 | |||
345 | while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) { | ||
346 | if (optval < 0) | ||
347 | return optval; | ||
348 | switch (optval) { | ||
349 | case 'u': | ||
350 | data->uid = optint; | ||
351 | break; | ||
352 | case 'g': | ||
353 | data->gid = optint; | ||
354 | break; | ||
355 | case 'o': | ||
356 | data->mounted_uid = optint; | ||
357 | break; | ||
358 | case 'm': | ||
359 | data->file_mode = optint; | ||
360 | break; | ||
361 | case 'd': | ||
362 | data->dir_mode = optint; | ||
363 | break; | ||
364 | case 't': | ||
365 | data->time_out = optint; | ||
366 | break; | ||
367 | case 'r': | ||
368 | data->retry_count = optint; | ||
369 | break; | ||
370 | case 'f': | ||
371 | data->flags = optint; | ||
372 | break; | ||
373 | case 'w': | ||
374 | data->wdog_pid = optint; | ||
375 | break; | ||
376 | case 'n': | ||
377 | data->ncp_fd = optint; | ||
378 | break; | ||
379 | case 'i': | ||
380 | data->info_fd = optint; | ||
381 | break; | ||
382 | case 'v': | ||
383 | if (optint < NCP_MOUNT_VERSION_V4) { | ||
384 | return -ECHRNG; | ||
385 | } | ||
386 | if (optint > NCP_MOUNT_VERSION_V5) { | ||
387 | return -ECHRNG; | ||
388 | } | ||
389 | version = optint; | ||
390 | break; | ||
391 | |||
392 | } | ||
393 | } | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) | ||
398 | { | ||
399 | struct ncp_mount_data_kernel data; | ||
400 | struct ncp_server *server; | ||
401 | struct file *ncp_filp; | ||
402 | struct inode *root_inode; | ||
403 | struct inode *sock_inode; | ||
404 | struct socket *sock; | ||
405 | int error; | ||
406 | int default_bufsize; | ||
407 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
408 | int options; | ||
409 | #endif | ||
410 | struct ncp_entry_info finfo; | ||
411 | |||
412 | server = kmalloc(sizeof(struct ncp_server), GFP_KERNEL); | ||
413 | if (!server) | ||
414 | return -ENOMEM; | ||
415 | sb->s_fs_info = server; | ||
416 | memset(server, 0, sizeof(struct ncp_server)); | ||
417 | |||
418 | error = -EFAULT; | ||
419 | if (raw_data == NULL) | ||
420 | goto out; | ||
421 | switch (*(int*)raw_data) { | ||
422 | case NCP_MOUNT_VERSION: | ||
423 | { | ||
424 | struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data; | ||
425 | |||
426 | data.flags = md->flags; | ||
427 | data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE; | ||
428 | data.mounted_uid = md->mounted_uid; | ||
429 | data.wdog_pid = md->wdog_pid; | ||
430 | data.ncp_fd = md->ncp_fd; | ||
431 | data.time_out = md->time_out; | ||
432 | data.retry_count = md->retry_count; | ||
433 | data.uid = md->uid; | ||
434 | data.gid = md->gid; | ||
435 | data.file_mode = md->file_mode; | ||
436 | data.dir_mode = md->dir_mode; | ||
437 | data.info_fd = -1; | ||
438 | memcpy(data.mounted_vol, md->mounted_vol, | ||
439 | NCP_VOLNAME_LEN+1); | ||
440 | } | ||
441 | break; | ||
442 | case NCP_MOUNT_VERSION_V4: | ||
443 | { | ||
444 | struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data; | ||
445 | |||
446 | data.flags = md->flags; | ||
447 | data.int_flags = 0; | ||
448 | data.mounted_uid = md->mounted_uid; | ||
449 | data.wdog_pid = md->wdog_pid; | ||
450 | data.ncp_fd = md->ncp_fd; | ||
451 | data.time_out = md->time_out; | ||
452 | data.retry_count = md->retry_count; | ||
453 | data.uid = md->uid; | ||
454 | data.gid = md->gid; | ||
455 | data.file_mode = md->file_mode; | ||
456 | data.dir_mode = md->dir_mode; | ||
457 | data.info_fd = -1; | ||
458 | data.mounted_vol[0] = 0; | ||
459 | } | ||
460 | break; | ||
461 | default: | ||
462 | error = -ECHRNG; | ||
463 | if (memcmp(raw_data, "vers", 4) == 0) { | ||
464 | error = ncp_parse_options(&data, raw_data); | ||
465 | } | ||
466 | if (error) | ||
467 | goto out; | ||
468 | break; | ||
469 | } | ||
470 | error = -EBADF; | ||
471 | ncp_filp = fget(data.ncp_fd); | ||
472 | if (!ncp_filp) | ||
473 | goto out; | ||
474 | error = -ENOTSOCK; | ||
475 | sock_inode = ncp_filp->f_dentry->d_inode; | ||
476 | if (!S_ISSOCK(sock_inode->i_mode)) | ||
477 | goto out_fput; | ||
478 | sock = SOCKET_I(sock_inode); | ||
479 | if (!sock) | ||
480 | goto out_fput; | ||
481 | |||
482 | if (sock->type == SOCK_STREAM) | ||
483 | default_bufsize = 0xF000; | ||
484 | else | ||
485 | default_bufsize = 1024; | ||
486 | |||
487 | sb->s_flags |= MS_NODIRATIME; /* probably even noatime */ | ||
488 | sb->s_maxbytes = 0xFFFFFFFFU; | ||
489 | sb->s_blocksize = 1024; /* Eh... Is this correct? */ | ||
490 | sb->s_blocksize_bits = 10; | ||
491 | sb->s_magic = NCP_SUPER_MAGIC; | ||
492 | sb->s_op = &ncp_sops; | ||
493 | |||
494 | server = NCP_SBP(sb); | ||
495 | memset(server, 0, sizeof(*server)); | ||
496 | |||
497 | server->ncp_filp = ncp_filp; | ||
498 | server->ncp_sock = sock; | ||
499 | |||
500 | if (data.info_fd != -1) { | ||
501 | struct socket *info_sock; | ||
502 | |||
503 | error = -EBADF; | ||
504 | server->info_filp = fget(data.info_fd); | ||
505 | if (!server->info_filp) | ||
506 | goto out_fput; | ||
507 | error = -ENOTSOCK; | ||
508 | sock_inode = server->info_filp->f_dentry->d_inode; | ||
509 | if (!S_ISSOCK(sock_inode->i_mode)) | ||
510 | goto out_fput2; | ||
511 | info_sock = SOCKET_I(sock_inode); | ||
512 | if (!info_sock) | ||
513 | goto out_fput2; | ||
514 | error = -EBADFD; | ||
515 | if (info_sock->type != SOCK_STREAM) | ||
516 | goto out_fput2; | ||
517 | server->info_sock = info_sock; | ||
518 | } | ||
519 | |||
520 | /* server->lock = 0; */ | ||
521 | init_MUTEX(&server->sem); | ||
522 | server->packet = NULL; | ||
523 | /* server->buffer_size = 0; */ | ||
524 | /* server->conn_status = 0; */ | ||
525 | /* server->root_dentry = NULL; */ | ||
526 | /* server->root_setuped = 0; */ | ||
527 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
528 | /* server->sign_wanted = 0; */ | ||
529 | /* server->sign_active = 0; */ | ||
530 | #endif | ||
531 | server->auth.auth_type = NCP_AUTH_NONE; | ||
532 | /* server->auth.object_name_len = 0; */ | ||
533 | /* server->auth.object_name = NULL; */ | ||
534 | /* server->auth.object_type = 0; */ | ||
535 | /* server->priv.len = 0; */ | ||
536 | /* server->priv.data = NULL; */ | ||
537 | |||
538 | server->m = data; | ||
539 | /* Althought anything producing this is buggy, it happens | ||
540 | now because of PATH_MAX changes.. */ | ||
541 | if (server->m.time_out < 1) { | ||
542 | server->m.time_out = 10; | ||
543 | printk(KERN_INFO "You need to recompile your ncpfs utils..\n"); | ||
544 | } | ||
545 | server->m.time_out = server->m.time_out * HZ / 100; | ||
546 | server->m.file_mode = (server->m.file_mode & S_IRWXUGO) | S_IFREG; | ||
547 | server->m.dir_mode = (server->m.dir_mode & S_IRWXUGO) | S_IFDIR; | ||
548 | |||
549 | #ifdef CONFIG_NCPFS_NLS | ||
550 | /* load the default NLS charsets */ | ||
551 | server->nls_vol = load_nls_default(); | ||
552 | server->nls_io = load_nls_default(); | ||
553 | #endif /* CONFIG_NCPFS_NLS */ | ||
554 | |||
555 | server->dentry_ttl = 0; /* no caching */ | ||
556 | |||
557 | INIT_LIST_HEAD(&server->tx.requests); | ||
558 | init_MUTEX(&server->rcv.creq_sem); | ||
559 | server->tx.creq = NULL; | ||
560 | server->rcv.creq = NULL; | ||
561 | server->data_ready = sock->sk->sk_data_ready; | ||
562 | server->write_space = sock->sk->sk_write_space; | ||
563 | server->error_report = sock->sk->sk_error_report; | ||
564 | sock->sk->sk_user_data = server; | ||
565 | |||
566 | init_timer(&server->timeout_tm); | ||
567 | #undef NCP_PACKET_SIZE | ||
568 | #define NCP_PACKET_SIZE 131072 | ||
569 | error = -ENOMEM; | ||
570 | server->packet_size = NCP_PACKET_SIZE; | ||
571 | server->packet = vmalloc(NCP_PACKET_SIZE); | ||
572 | if (server->packet == NULL) | ||
573 | goto out_nls; | ||
574 | |||
575 | sock->sk->sk_data_ready = ncp_tcp_data_ready; | ||
576 | sock->sk->sk_error_report = ncp_tcp_error_report; | ||
577 | if (sock->type == SOCK_STREAM) { | ||
578 | server->rcv.ptr = (unsigned char*)&server->rcv.buf; | ||
579 | server->rcv.len = 10; | ||
580 | server->rcv.state = 0; | ||
581 | INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc, server); | ||
582 | INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc, server); | ||
583 | sock->sk->sk_write_space = ncp_tcp_write_space; | ||
584 | } else { | ||
585 | INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc, server); | ||
586 | INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc, server); | ||
587 | server->timeout_tm.data = (unsigned long)server; | ||
588 | server->timeout_tm.function = ncpdgram_timeout_call; | ||
589 | } | ||
590 | |||
591 | ncp_lock_server(server); | ||
592 | error = ncp_connect(server); | ||
593 | ncp_unlock_server(server); | ||
594 | if (error < 0) | ||
595 | goto out_packet; | ||
596 | DPRINTK("ncp_fill_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb)); | ||
597 | |||
598 | error = -EMSGSIZE; /* -EREMOTESIDEINCOMPATIBLE */ | ||
599 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
600 | if (ncp_negotiate_size_and_options(server, default_bufsize, | ||
601 | NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0) | ||
602 | { | ||
603 | if (options != NCP_DEFAULT_OPTIONS) | ||
604 | { | ||
605 | if (ncp_negotiate_size_and_options(server, | ||
606 | default_bufsize, | ||
607 | options & 2, | ||
608 | &(server->buffer_size), &options) != 0) | ||
609 | |||
610 | { | ||
611 | goto out_disconnect; | ||
612 | } | ||
613 | } | ||
614 | if (options & 2) | ||
615 | server->sign_wanted = 1; | ||
616 | } | ||
617 | else | ||
618 | #endif /* CONFIG_NCPFS_PACKET_SIGNING */ | ||
619 | if (ncp_negotiate_buffersize(server, default_bufsize, | ||
620 | &(server->buffer_size)) != 0) | ||
621 | goto out_disconnect; | ||
622 | DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size); | ||
623 | |||
624 | memset(&finfo, 0, sizeof(finfo)); | ||
625 | finfo.i.attributes = aDIR; | ||
626 | finfo.i.dataStreamSize = 0; /* ignored */ | ||
627 | finfo.i.dirEntNum = 0; | ||
628 | finfo.i.DosDirNum = 0; | ||
629 | #ifdef CONFIG_NCPFS_SMALLDOS | ||
630 | finfo.i.NSCreator = NW_NS_DOS; | ||
631 | #endif | ||
632 | finfo.volume = NCP_NUMBER_OF_VOLUMES; | ||
633 | /* set dates of mountpoint to Jan 1, 1986; 00:00 */ | ||
634 | finfo.i.creationTime = finfo.i.modifyTime | ||
635 | = cpu_to_le16(0x0000); | ||
636 | finfo.i.creationDate = finfo.i.modifyDate | ||
637 | = finfo.i.lastAccessDate | ||
638 | = cpu_to_le16(0x0C21); | ||
639 | finfo.i.nameLen = 0; | ||
640 | finfo.i.entryName[0] = '\0'; | ||
641 | |||
642 | finfo.opened = 0; | ||
643 | finfo.ino = 2; /* tradition */ | ||
644 | |||
645 | server->name_space[finfo.volume] = NW_NS_DOS; | ||
646 | |||
647 | error = -ENOMEM; | ||
648 | root_inode = ncp_iget(sb, &finfo); | ||
649 | if (!root_inode) | ||
650 | goto out_disconnect; | ||
651 | DPRINTK("ncp_fill_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber); | ||
652 | sb->s_root = d_alloc_root(root_inode); | ||
653 | if (!sb->s_root) | ||
654 | goto out_no_root; | ||
655 | sb->s_root->d_op = &ncp_root_dentry_operations; | ||
656 | return 0; | ||
657 | |||
658 | out_no_root: | ||
659 | iput(root_inode); | ||
660 | out_disconnect: | ||
661 | ncp_lock_server(server); | ||
662 | ncp_disconnect(server); | ||
663 | ncp_unlock_server(server); | ||
664 | out_packet: | ||
665 | ncp_stop_tasks(server); | ||
666 | vfree(server->packet); | ||
667 | out_nls: | ||
668 | #ifdef CONFIG_NCPFS_NLS | ||
669 | unload_nls(server->nls_io); | ||
670 | unload_nls(server->nls_vol); | ||
671 | #endif | ||
672 | out_fput2: | ||
673 | if (server->info_filp) | ||
674 | fput(server->info_filp); | ||
675 | out_fput: | ||
676 | /* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>: | ||
677 | * | ||
678 | * The previously used put_filp(ncp_filp); was bogous, since | ||
679 | * it doesn't proper unlocking. | ||
680 | */ | ||
681 | fput(ncp_filp); | ||
682 | out: | ||
683 | sb->s_fs_info = NULL; | ||
684 | kfree(server); | ||
685 | return error; | ||
686 | } | ||
687 | |||
688 | static void ncp_put_super(struct super_block *sb) | ||
689 | { | ||
690 | struct ncp_server *server = NCP_SBP(sb); | ||
691 | |||
692 | ncp_lock_server(server); | ||
693 | ncp_disconnect(server); | ||
694 | ncp_unlock_server(server); | ||
695 | |||
696 | ncp_stop_tasks(server); | ||
697 | |||
698 | #ifdef CONFIG_NCPFS_NLS | ||
699 | /* unload the NLS charsets */ | ||
700 | if (server->nls_vol) | ||
701 | { | ||
702 | unload_nls(server->nls_vol); | ||
703 | server->nls_vol = NULL; | ||
704 | } | ||
705 | if (server->nls_io) | ||
706 | { | ||
707 | unload_nls(server->nls_io); | ||
708 | server->nls_io = NULL; | ||
709 | } | ||
710 | #endif /* CONFIG_NCPFS_NLS */ | ||
711 | |||
712 | if (server->info_filp) | ||
713 | fput(server->info_filp); | ||
714 | fput(server->ncp_filp); | ||
715 | kill_proc(server->m.wdog_pid, SIGTERM, 1); | ||
716 | |||
717 | if (server->priv.data) | ||
718 | ncp_kfree_s(server->priv.data, server->priv.len); | ||
719 | if (server->auth.object_name) | ||
720 | ncp_kfree_s(server->auth.object_name, server->auth.object_name_len); | ||
721 | vfree(server->packet); | ||
722 | sb->s_fs_info = NULL; | ||
723 | kfree(server); | ||
724 | } | ||
725 | |||
726 | static int ncp_statfs(struct super_block *sb, struct kstatfs *buf) | ||
727 | { | ||
728 | struct dentry* d; | ||
729 | struct inode* i; | ||
730 | struct ncp_inode_info* ni; | ||
731 | struct ncp_server* s; | ||
732 | struct ncp_volume_info vi; | ||
733 | int err; | ||
734 | __u8 dh; | ||
735 | |||
736 | d = sb->s_root; | ||
737 | if (!d) { | ||
738 | goto dflt; | ||
739 | } | ||
740 | i = d->d_inode; | ||
741 | if (!i) { | ||
742 | goto dflt; | ||
743 | } | ||
744 | ni = NCP_FINFO(i); | ||
745 | if (!ni) { | ||
746 | goto dflt; | ||
747 | } | ||
748 | s = NCP_SBP(sb); | ||
749 | if (!s) { | ||
750 | goto dflt; | ||
751 | } | ||
752 | if (!s->m.mounted_vol[0]) { | ||
753 | goto dflt; | ||
754 | } | ||
755 | |||
756 | err = ncp_dirhandle_alloc(s, ni->volNumber, ni->DosDirNum, &dh); | ||
757 | if (err) { | ||
758 | goto dflt; | ||
759 | } | ||
760 | err = ncp_get_directory_info(s, dh, &vi); | ||
761 | ncp_dirhandle_free(s, dh); | ||
762 | if (err) { | ||
763 | goto dflt; | ||
764 | } | ||
765 | buf->f_type = NCP_SUPER_MAGIC; | ||
766 | buf->f_bsize = vi.sectors_per_block * 512; | ||
767 | buf->f_blocks = vi.total_blocks; | ||
768 | buf->f_bfree = vi.free_blocks; | ||
769 | buf->f_bavail = vi.free_blocks; | ||
770 | buf->f_files = vi.total_dir_entries; | ||
771 | buf->f_ffree = vi.available_dir_entries; | ||
772 | buf->f_namelen = 12; | ||
773 | return 0; | ||
774 | |||
775 | /* We cannot say how much disk space is left on a mounted | ||
776 | NetWare Server, because free space is distributed over | ||
777 | volumes, and the current user might have disk quotas. So | ||
778 | free space is not that simple to determine. Our decision | ||
779 | here is to err conservatively. */ | ||
780 | |||
781 | dflt:; | ||
782 | buf->f_type = NCP_SUPER_MAGIC; | ||
783 | buf->f_bsize = NCP_BLOCK_SIZE; | ||
784 | buf->f_blocks = 0; | ||
785 | buf->f_bfree = 0; | ||
786 | buf->f_bavail = 0; | ||
787 | buf->f_namelen = 12; | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | int ncp_notify_change(struct dentry *dentry, struct iattr *attr) | ||
792 | { | ||
793 | struct inode *inode = dentry->d_inode; | ||
794 | int result = 0; | ||
795 | __le32 info_mask; | ||
796 | struct nw_modify_dos_info info; | ||
797 | struct ncp_server *server; | ||
798 | |||
799 | result = -EIO; | ||
800 | |||
801 | lock_kernel(); | ||
802 | |||
803 | server = NCP_SERVER(inode); | ||
804 | if ((!server) || !ncp_conn_valid(server)) | ||
805 | goto out; | ||
806 | |||
807 | /* ageing the dentry to force validation */ | ||
808 | ncp_age_dentry(server, dentry); | ||
809 | |||
810 | result = inode_change_ok(inode, attr); | ||
811 | if (result < 0) | ||
812 | goto out; | ||
813 | |||
814 | result = -EPERM; | ||
815 | if (((attr->ia_valid & ATTR_UID) && | ||
816 | (attr->ia_uid != server->m.uid))) | ||
817 | goto out; | ||
818 | |||
819 | if (((attr->ia_valid & ATTR_GID) && | ||
820 | (attr->ia_gid != server->m.gid))) | ||
821 | goto out; | ||
822 | |||
823 | if (((attr->ia_valid & ATTR_MODE) && | ||
824 | (attr->ia_mode & | ||
825 | ~(S_IFREG | S_IFDIR | S_IRWXUGO)))) | ||
826 | goto out; | ||
827 | |||
828 | info_mask = 0; | ||
829 | memset(&info, 0, sizeof(info)); | ||
830 | |||
831 | #if 1 | ||
832 | if ((attr->ia_valid & ATTR_MODE) != 0) | ||
833 | { | ||
834 | umode_t newmode = attr->ia_mode; | ||
835 | |||
836 | info_mask |= DM_ATTRIBUTES; | ||
837 | |||
838 | if (S_ISDIR(inode->i_mode)) { | ||
839 | newmode &= server->m.dir_mode; | ||
840 | } else { | ||
841 | #ifdef CONFIG_NCPFS_EXTRAS | ||
842 | if (server->m.flags & NCP_MOUNT_EXTRAS) { | ||
843 | /* any non-default execute bit set */ | ||
844 | if (newmode & ~server->m.file_mode & S_IXUGO) | ||
845 | info.attributes |= aSHARED | aSYSTEM; | ||
846 | /* read for group/world and not in default file_mode */ | ||
847 | else if (newmode & ~server->m.file_mode & S_IRUGO) | ||
848 | info.attributes |= aSHARED; | ||
849 | } else | ||
850 | #endif | ||
851 | newmode &= server->m.file_mode; | ||
852 | } | ||
853 | if (newmode & S_IWUGO) | ||
854 | info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); | ||
855 | else | ||
856 | info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT); | ||
857 | |||
858 | #ifdef CONFIG_NCPFS_NFS_NS | ||
859 | if (ncp_is_nfs_extras(server, NCP_FINFO(inode)->volNumber)) { | ||
860 | result = ncp_modify_nfs_info(server, | ||
861 | NCP_FINFO(inode)->volNumber, | ||
862 | NCP_FINFO(inode)->dirEntNum, | ||
863 | attr->ia_mode, 0); | ||
864 | if (result != 0) | ||
865 | goto out; | ||
866 | info.attributes &= ~(aSHARED | aSYSTEM); | ||
867 | { | ||
868 | /* mark partial success */ | ||
869 | struct iattr tmpattr; | ||
870 | |||
871 | tmpattr.ia_valid = ATTR_MODE; | ||
872 | tmpattr.ia_mode = attr->ia_mode; | ||
873 | |||
874 | result = inode_setattr(inode, &tmpattr); | ||
875 | if (result) | ||
876 | goto out; | ||
877 | } | ||
878 | } | ||
879 | #endif | ||
880 | } | ||
881 | #endif | ||
882 | |||
883 | /* Do SIZE before attributes, otherwise mtime together with size does not work... | ||
884 | */ | ||
885 | if ((attr->ia_valid & ATTR_SIZE) != 0) { | ||
886 | int written; | ||
887 | |||
888 | DPRINTK("ncpfs: trying to change size to %ld\n", | ||
889 | attr->ia_size); | ||
890 | |||
891 | if ((result = ncp_make_open(inode, O_WRONLY)) < 0) { | ||
892 | result = -EACCES; | ||
893 | goto out; | ||
894 | } | ||
895 | ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, | ||
896 | attr->ia_size, 0, "", &written); | ||
897 | |||
898 | /* According to ndir, the changes only take effect after | ||
899 | closing the file */ | ||
900 | ncp_inode_close(inode); | ||
901 | result = ncp_make_closed(inode); | ||
902 | if (result) | ||
903 | goto out; | ||
904 | { | ||
905 | struct iattr tmpattr; | ||
906 | |||
907 | tmpattr.ia_valid = ATTR_SIZE; | ||
908 | tmpattr.ia_size = attr->ia_size; | ||
909 | |||
910 | result = inode_setattr(inode, &tmpattr); | ||
911 | if (result) | ||
912 | goto out; | ||
913 | } | ||
914 | } | ||
915 | if ((attr->ia_valid & ATTR_CTIME) != 0) { | ||
916 | info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE); | ||
917 | ncp_date_unix2dos(attr->ia_ctime.tv_sec, | ||
918 | &info.creationTime, &info.creationDate); | ||
919 | } | ||
920 | if ((attr->ia_valid & ATTR_MTIME) != 0) { | ||
921 | info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE); | ||
922 | ncp_date_unix2dos(attr->ia_mtime.tv_sec, | ||
923 | &info.modifyTime, &info.modifyDate); | ||
924 | } | ||
925 | if ((attr->ia_valid & ATTR_ATIME) != 0) { | ||
926 | __le16 dummy; | ||
927 | info_mask |= (DM_LAST_ACCESS_DATE); | ||
928 | ncp_date_unix2dos(attr->ia_atime.tv_sec, | ||
929 | &dummy, &info.lastAccessDate); | ||
930 | } | ||
931 | if (info_mask != 0) { | ||
932 | result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), | ||
933 | inode, info_mask, &info); | ||
934 | if (result != 0) { | ||
935 | result = -EACCES; | ||
936 | |||
937 | if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) { | ||
938 | /* NetWare seems not to allow this. I | ||
939 | do not know why. So, just tell the | ||
940 | user everything went fine. This is | ||
941 | a terrible hack, but I do not know | ||
942 | how to do this correctly. */ | ||
943 | result = 0; | ||
944 | } else | ||
945 | goto out; | ||
946 | } | ||
947 | #ifdef CONFIG_NCPFS_STRONG | ||
948 | if ((!result) && (info_mask & DM_ATTRIBUTES)) | ||
949 | NCP_FINFO(inode)->nwattr = info.attributes; | ||
950 | #endif | ||
951 | } | ||
952 | if (!result) | ||
953 | result = inode_setattr(inode, attr); | ||
954 | out: | ||
955 | unlock_kernel(); | ||
956 | return result; | ||
957 | } | ||
958 | |||
959 | #ifdef DEBUG_NCP_MALLOC | ||
960 | int ncp_malloced; | ||
961 | int ncp_current_malloced; | ||
962 | #endif | ||
963 | |||
964 | static struct super_block *ncp_get_sb(struct file_system_type *fs_type, | ||
965 | int flags, const char *dev_name, void *data) | ||
966 | { | ||
967 | return get_sb_nodev(fs_type, flags, data, ncp_fill_super); | ||
968 | } | ||
969 | |||
970 | static struct file_system_type ncp_fs_type = { | ||
971 | .owner = THIS_MODULE, | ||
972 | .name = "ncpfs", | ||
973 | .get_sb = ncp_get_sb, | ||
974 | .kill_sb = kill_anon_super, | ||
975 | }; | ||
976 | |||
977 | static int __init init_ncp_fs(void) | ||
978 | { | ||
979 | int err; | ||
980 | DPRINTK("ncpfs: init_module called\n"); | ||
981 | |||
982 | #ifdef DEBUG_NCP_MALLOC | ||
983 | ncp_malloced = 0; | ||
984 | ncp_current_malloced = 0; | ||
985 | #endif | ||
986 | err = init_inodecache(); | ||
987 | if (err) | ||
988 | goto out1; | ||
989 | err = register_filesystem(&ncp_fs_type); | ||
990 | if (err) | ||
991 | goto out; | ||
992 | return 0; | ||
993 | out: | ||
994 | destroy_inodecache(); | ||
995 | out1: | ||
996 | return err; | ||
997 | } | ||
998 | |||
999 | static void __exit exit_ncp_fs(void) | ||
1000 | { | ||
1001 | DPRINTK("ncpfs: cleanup_module called\n"); | ||
1002 | unregister_filesystem(&ncp_fs_type); | ||
1003 | destroy_inodecache(); | ||
1004 | #ifdef DEBUG_NCP_MALLOC | ||
1005 | PRINTK("ncp_malloced: %d\n", ncp_malloced); | ||
1006 | PRINTK("ncp_current_malloced: %d\n", ncp_current_malloced); | ||
1007 | #endif | ||
1008 | } | ||
1009 | |||
1010 | module_init(init_ncp_fs) | ||
1011 | module_exit(exit_ncp_fs) | ||
1012 | MODULE_LICENSE("GPL"); | ||
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c new file mode 100644 index 000000000000..88df79356a1f --- /dev/null +++ b/fs/ncpfs/ioctl.c | |||
@@ -0,0 +1,649 @@ | |||
1 | /* | ||
2 | * ioctl.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 by Volker Lendecke | ||
5 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
6 | * Modified 1998, 1999 Wolfram Pienkoss for NLS | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | |||
12 | #include <asm/uaccess.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/fs.h> | ||
15 | #include <linux/ioctl.h> | ||
16 | #include <linux/time.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/highuid.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | |||
21 | #include <linux/ncp_fs.h> | ||
22 | |||
23 | #include "ncplib_kernel.h" | ||
24 | |||
25 | /* maximum limit for ncp_objectname_ioctl */ | ||
26 | #define NCP_OBJECT_NAME_MAX_LEN 4096 | ||
27 | /* maximum limit for ncp_privatedata_ioctl */ | ||
28 | #define NCP_PRIVATE_DATA_MAX_LEN 8192 | ||
29 | /* maximum negotiable packet size */ | ||
30 | #define NCP_PACKET_SIZE_INTERNAL 65536 | ||
31 | |||
32 | static int | ||
33 | ncp_get_fs_info(struct ncp_server* server, struct inode* inode, struct ncp_fs_info __user *arg) | ||
34 | { | ||
35 | struct ncp_fs_info info; | ||
36 | |||
37 | if ((permission(inode, MAY_WRITE, NULL) != 0) | ||
38 | && (current->uid != server->m.mounted_uid)) { | ||
39 | return -EACCES; | ||
40 | } | ||
41 | if (copy_from_user(&info, arg, sizeof(info))) | ||
42 | return -EFAULT; | ||
43 | |||
44 | if (info.version != NCP_GET_FS_INFO_VERSION) { | ||
45 | DPRINTK("info.version invalid: %d\n", info.version); | ||
46 | return -EINVAL; | ||
47 | } | ||
48 | /* TODO: info.addr = server->m.serv_addr; */ | ||
49 | SET_UID(info.mounted_uid, server->m.mounted_uid); | ||
50 | info.connection = server->connection; | ||
51 | info.buffer_size = server->buffer_size; | ||
52 | info.volume_number = NCP_FINFO(inode)->volNumber; | ||
53 | info.directory_id = NCP_FINFO(inode)->DosDirNum; | ||
54 | |||
55 | if (copy_to_user(arg, &info, sizeof(info))) | ||
56 | return -EFAULT; | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static int | ||
61 | ncp_get_fs_info_v2(struct ncp_server* server, struct inode* inode, struct ncp_fs_info_v2 __user * arg) | ||
62 | { | ||
63 | struct ncp_fs_info_v2 info2; | ||
64 | |||
65 | if ((permission(inode, MAY_WRITE, NULL) != 0) | ||
66 | && (current->uid != server->m.mounted_uid)) { | ||
67 | return -EACCES; | ||
68 | } | ||
69 | if (copy_from_user(&info2, arg, sizeof(info2))) | ||
70 | return -EFAULT; | ||
71 | |||
72 | if (info2.version != NCP_GET_FS_INFO_VERSION_V2) { | ||
73 | DPRINTK("info.version invalid: %d\n", info2.version); | ||
74 | return -EINVAL; | ||
75 | } | ||
76 | info2.mounted_uid = server->m.mounted_uid; | ||
77 | info2.connection = server->connection; | ||
78 | info2.buffer_size = server->buffer_size; | ||
79 | info2.volume_number = NCP_FINFO(inode)->volNumber; | ||
80 | info2.directory_id = NCP_FINFO(inode)->DosDirNum; | ||
81 | info2.dummy1 = info2.dummy2 = info2.dummy3 = 0; | ||
82 | |||
83 | if (copy_to_user(arg, &info2, sizeof(info2))) | ||
84 | return -EFAULT; | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | #ifdef CONFIG_NCPFS_NLS | ||
89 | /* Here we are select the iocharset and the codepage for NLS. | ||
90 | * Thanks Petr Vandrovec for idea and many hints. | ||
91 | */ | ||
92 | static int | ||
93 | ncp_set_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) | ||
94 | { | ||
95 | struct ncp_nls_ioctl user; | ||
96 | struct nls_table *codepage; | ||
97 | struct nls_table *iocharset; | ||
98 | struct nls_table *oldset_io; | ||
99 | struct nls_table *oldset_cp; | ||
100 | |||
101 | if (!capable(CAP_SYS_ADMIN)) | ||
102 | return -EACCES; | ||
103 | if (server->root_setuped) | ||
104 | return -EBUSY; | ||
105 | |||
106 | if (copy_from_user(&user, arg, sizeof(user))) | ||
107 | return -EFAULT; | ||
108 | |||
109 | codepage = NULL; | ||
110 | user.codepage[NCP_IOCSNAME_LEN] = 0; | ||
111 | if (!user.codepage[0] || !strcmp(user.codepage, "default")) | ||
112 | codepage = load_nls_default(); | ||
113 | else { | ||
114 | codepage = load_nls(user.codepage); | ||
115 | if (!codepage) { | ||
116 | return -EBADRQC; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | iocharset = NULL; | ||
121 | user.iocharset[NCP_IOCSNAME_LEN] = 0; | ||
122 | if (!user.iocharset[0] || !strcmp(user.iocharset, "default")) { | ||
123 | iocharset = load_nls_default(); | ||
124 | NCP_CLR_FLAG(server, NCP_FLAG_UTF8); | ||
125 | } else if (!strcmp(user.iocharset, "utf8")) { | ||
126 | iocharset = load_nls_default(); | ||
127 | NCP_SET_FLAG(server, NCP_FLAG_UTF8); | ||
128 | } else { | ||
129 | iocharset = load_nls(user.iocharset); | ||
130 | if (!iocharset) { | ||
131 | unload_nls(codepage); | ||
132 | return -EBADRQC; | ||
133 | } | ||
134 | NCP_CLR_FLAG(server, NCP_FLAG_UTF8); | ||
135 | } | ||
136 | |||
137 | oldset_cp = server->nls_vol; | ||
138 | server->nls_vol = codepage; | ||
139 | oldset_io = server->nls_io; | ||
140 | server->nls_io = iocharset; | ||
141 | |||
142 | if (oldset_cp) | ||
143 | unload_nls(oldset_cp); | ||
144 | if (oldset_io) | ||
145 | unload_nls(oldset_io); | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int | ||
151 | ncp_get_charsets(struct ncp_server* server, struct ncp_nls_ioctl __user *arg) | ||
152 | { | ||
153 | struct ncp_nls_ioctl user; | ||
154 | int len; | ||
155 | |||
156 | memset(&user, 0, sizeof(user)); | ||
157 | if (server->nls_vol && server->nls_vol->charset) { | ||
158 | len = strlen(server->nls_vol->charset); | ||
159 | if (len > NCP_IOCSNAME_LEN) | ||
160 | len = NCP_IOCSNAME_LEN; | ||
161 | strncpy(user.codepage, server->nls_vol->charset, len); | ||
162 | user.codepage[len] = 0; | ||
163 | } | ||
164 | |||
165 | if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) | ||
166 | strcpy(user.iocharset, "utf8"); | ||
167 | else if (server->nls_io && server->nls_io->charset) { | ||
168 | len = strlen(server->nls_io->charset); | ||
169 | if (len > NCP_IOCSNAME_LEN) | ||
170 | len = NCP_IOCSNAME_LEN; | ||
171 | strncpy(user.iocharset, server->nls_io->charset, len); | ||
172 | user.iocharset[len] = 0; | ||
173 | } | ||
174 | |||
175 | if (copy_to_user(arg, &user, sizeof(user))) | ||
176 | return -EFAULT; | ||
177 | return 0; | ||
178 | } | ||
179 | #endif /* CONFIG_NCPFS_NLS */ | ||
180 | |||
181 | int ncp_ioctl(struct inode *inode, struct file *filp, | ||
182 | unsigned int cmd, unsigned long arg) | ||
183 | { | ||
184 | struct ncp_server *server = NCP_SERVER(inode); | ||
185 | int result; | ||
186 | struct ncp_ioctl_request request; | ||
187 | char* bouncebuffer; | ||
188 | void __user *argp = (void __user *)arg; | ||
189 | |||
190 | switch (cmd) { | ||
191 | case NCP_IOC_NCPREQUEST: | ||
192 | |||
193 | if ((permission(inode, MAY_WRITE, NULL) != 0) | ||
194 | && (current->uid != server->m.mounted_uid)) { | ||
195 | return -EACCES; | ||
196 | } | ||
197 | if (copy_from_user(&request, argp, sizeof(request))) | ||
198 | return -EFAULT; | ||
199 | |||
200 | if ((request.function > 255) | ||
201 | || (request.size > | ||
202 | NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) { | ||
203 | return -EINVAL; | ||
204 | } | ||
205 | bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL); | ||
206 | if (!bouncebuffer) | ||
207 | return -ENOMEM; | ||
208 | if (copy_from_user(bouncebuffer, request.data, request.size)) { | ||
209 | vfree(bouncebuffer); | ||
210 | return -EFAULT; | ||
211 | } | ||
212 | ncp_lock_server(server); | ||
213 | |||
214 | /* FIXME: We hack around in the server's structures | ||
215 | here to be able to use ncp_request */ | ||
216 | |||
217 | server->has_subfunction = 0; | ||
218 | server->current_size = request.size; | ||
219 | memcpy(server->packet, bouncebuffer, request.size); | ||
220 | |||
221 | result = ncp_request2(server, request.function, | ||
222 | bouncebuffer, NCP_PACKET_SIZE_INTERNAL); | ||
223 | if (result < 0) | ||
224 | result = -EIO; | ||
225 | else | ||
226 | result = server->reply_size; | ||
227 | ncp_unlock_server(server); | ||
228 | DPRINTK("ncp_ioctl: copy %d bytes\n", | ||
229 | result); | ||
230 | if (result >= 0) | ||
231 | if (copy_to_user(request.data, bouncebuffer, result)) | ||
232 | result = -EFAULT; | ||
233 | vfree(bouncebuffer); | ||
234 | return result; | ||
235 | |||
236 | case NCP_IOC_CONN_LOGGED_IN: | ||
237 | |||
238 | if (!capable(CAP_SYS_ADMIN)) | ||
239 | return -EACCES; | ||
240 | if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE)) | ||
241 | return -EINVAL; | ||
242 | if (server->root_setuped) | ||
243 | return -EBUSY; | ||
244 | server->root_setuped = 1; | ||
245 | return ncp_conn_logged_in(inode->i_sb); | ||
246 | |||
247 | case NCP_IOC_GET_FS_INFO: | ||
248 | return ncp_get_fs_info(server, inode, argp); | ||
249 | |||
250 | case NCP_IOC_GET_FS_INFO_V2: | ||
251 | return ncp_get_fs_info_v2(server, inode, argp); | ||
252 | |||
253 | case NCP_IOC_GETMOUNTUID2: | ||
254 | { | ||
255 | unsigned long tmp = server->m.mounted_uid; | ||
256 | |||
257 | if ( (permission(inode, MAY_READ, NULL) != 0) | ||
258 | && (current->uid != server->m.mounted_uid)) | ||
259 | { | ||
260 | return -EACCES; | ||
261 | } | ||
262 | if (put_user(tmp, (unsigned long __user *)argp)) | ||
263 | return -EFAULT; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | case NCP_IOC_GETROOT: | ||
268 | { | ||
269 | struct ncp_setroot_ioctl sr; | ||
270 | |||
271 | if ( (permission(inode, MAY_READ, NULL) != 0) | ||
272 | && (current->uid != server->m.mounted_uid)) | ||
273 | { | ||
274 | return -EACCES; | ||
275 | } | ||
276 | if (server->m.mounted_vol[0]) { | ||
277 | struct dentry* dentry = inode->i_sb->s_root; | ||
278 | |||
279 | if (dentry) { | ||
280 | struct inode* inode = dentry->d_inode; | ||
281 | |||
282 | if (inode) { | ||
283 | sr.volNumber = NCP_FINFO(inode)->volNumber; | ||
284 | sr.dirEntNum = NCP_FINFO(inode)->dirEntNum; | ||
285 | sr.namespace = server->name_space[sr.volNumber]; | ||
286 | } else | ||
287 | DPRINTK("ncpfs: s_root->d_inode==NULL\n"); | ||
288 | } else | ||
289 | DPRINTK("ncpfs: s_root==NULL\n"); | ||
290 | } else { | ||
291 | sr.volNumber = -1; | ||
292 | sr.namespace = 0; | ||
293 | sr.dirEntNum = 0; | ||
294 | } | ||
295 | if (copy_to_user(argp, &sr, sizeof(sr))) | ||
296 | return -EFAULT; | ||
297 | return 0; | ||
298 | } | ||
299 | case NCP_IOC_SETROOT: | ||
300 | { | ||
301 | struct ncp_setroot_ioctl sr; | ||
302 | __u32 vnum; | ||
303 | __le32 de; | ||
304 | __le32 dosde; | ||
305 | struct dentry* dentry; | ||
306 | |||
307 | if (!capable(CAP_SYS_ADMIN)) | ||
308 | { | ||
309 | return -EACCES; | ||
310 | } | ||
311 | if (server->root_setuped) return -EBUSY; | ||
312 | if (copy_from_user(&sr, argp, sizeof(sr))) | ||
313 | return -EFAULT; | ||
314 | if (sr.volNumber < 0) { | ||
315 | server->m.mounted_vol[0] = 0; | ||
316 | vnum = NCP_NUMBER_OF_VOLUMES; | ||
317 | de = 0; | ||
318 | dosde = 0; | ||
319 | } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) { | ||
320 | return -EINVAL; | ||
321 | } else if (ncp_mount_subdir(server, sr.volNumber, | ||
322 | sr.namespace, sr.dirEntNum, | ||
323 | &vnum, &de, &dosde)) { | ||
324 | return -ENOENT; | ||
325 | } | ||
326 | |||
327 | dentry = inode->i_sb->s_root; | ||
328 | server->root_setuped = 1; | ||
329 | if (dentry) { | ||
330 | struct inode* inode = dentry->d_inode; | ||
331 | |||
332 | if (inode) { | ||
333 | NCP_FINFO(inode)->volNumber = vnum; | ||
334 | NCP_FINFO(inode)->dirEntNum = de; | ||
335 | NCP_FINFO(inode)->DosDirNum = dosde; | ||
336 | } else | ||
337 | DPRINTK("ncpfs: s_root->d_inode==NULL\n"); | ||
338 | } else | ||
339 | DPRINTK("ncpfs: s_root==NULL\n"); | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
345 | case NCP_IOC_SIGN_INIT: | ||
346 | if ((permission(inode, MAY_WRITE, NULL) != 0) | ||
347 | && (current->uid != server->m.mounted_uid)) | ||
348 | { | ||
349 | return -EACCES; | ||
350 | } | ||
351 | if (argp) { | ||
352 | if (server->sign_wanted) | ||
353 | { | ||
354 | struct ncp_sign_init sign; | ||
355 | |||
356 | if (copy_from_user(&sign, argp, sizeof(sign))) | ||
357 | return -EFAULT; | ||
358 | memcpy(server->sign_root,sign.sign_root,8); | ||
359 | memcpy(server->sign_last,sign.sign_last,16); | ||
360 | server->sign_active = 1; | ||
361 | } | ||
362 | /* ignore when signatures not wanted */ | ||
363 | } else { | ||
364 | server->sign_active = 0; | ||
365 | } | ||
366 | return 0; | ||
367 | |||
368 | case NCP_IOC_SIGN_WANTED: | ||
369 | if ( (permission(inode, MAY_READ, NULL) != 0) | ||
370 | && (current->uid != server->m.mounted_uid)) | ||
371 | { | ||
372 | return -EACCES; | ||
373 | } | ||
374 | |||
375 | if (put_user(server->sign_wanted, (int __user *)argp)) | ||
376 | return -EFAULT; | ||
377 | return 0; | ||
378 | case NCP_IOC_SET_SIGN_WANTED: | ||
379 | { | ||
380 | int newstate; | ||
381 | |||
382 | if ( (permission(inode, MAY_WRITE, NULL) != 0) | ||
383 | && (current->uid != server->m.mounted_uid)) | ||
384 | { | ||
385 | return -EACCES; | ||
386 | } | ||
387 | /* get only low 8 bits... */ | ||
388 | if (get_user(newstate, (unsigned char __user *)argp)) | ||
389 | return -EFAULT; | ||
390 | if (server->sign_active) { | ||
391 | /* cannot turn signatures OFF when active */ | ||
392 | if (!newstate) return -EINVAL; | ||
393 | } else { | ||
394 | server->sign_wanted = newstate != 0; | ||
395 | } | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | #endif /* CONFIG_NCPFS_PACKET_SIGNING */ | ||
400 | |||
401 | #ifdef CONFIG_NCPFS_IOCTL_LOCKING | ||
402 | case NCP_IOC_LOCKUNLOCK: | ||
403 | if ( (permission(inode, MAY_WRITE, NULL) != 0) | ||
404 | && (current->uid != server->m.mounted_uid)) | ||
405 | { | ||
406 | return -EACCES; | ||
407 | } | ||
408 | { | ||
409 | struct ncp_lock_ioctl rqdata; | ||
410 | int result; | ||
411 | |||
412 | if (copy_from_user(&rqdata, argp, sizeof(rqdata))) | ||
413 | return -EFAULT; | ||
414 | if (rqdata.origin != 0) | ||
415 | return -EINVAL; | ||
416 | /* check for cmd */ | ||
417 | switch (rqdata.cmd) { | ||
418 | case NCP_LOCK_EX: | ||
419 | case NCP_LOCK_SH: | ||
420 | if (rqdata.timeout == 0) | ||
421 | rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; | ||
422 | else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT) | ||
423 | rqdata.timeout = NCP_LOCK_MAX_TIMEOUT; | ||
424 | break; | ||
425 | case NCP_LOCK_LOG: | ||
426 | rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */ | ||
427 | case NCP_LOCK_CLEAR: | ||
428 | break; | ||
429 | default: | ||
430 | return -EINVAL; | ||
431 | } | ||
432 | /* locking needs both read and write access */ | ||
433 | if ((result = ncp_make_open(inode, O_RDWR)) != 0) | ||
434 | { | ||
435 | return result; | ||
436 | } | ||
437 | result = -EIO; | ||
438 | if (!ncp_conn_valid(server)) | ||
439 | goto outrel; | ||
440 | result = -EISDIR; | ||
441 | if (!S_ISREG(inode->i_mode)) | ||
442 | goto outrel; | ||
443 | if (rqdata.cmd == NCP_LOCK_CLEAR) | ||
444 | { | ||
445 | result = ncp_ClearPhysicalRecord(NCP_SERVER(inode), | ||
446 | NCP_FINFO(inode)->file_handle, | ||
447 | rqdata.offset, | ||
448 | rqdata.length); | ||
449 | if (result > 0) result = 0; /* no such lock */ | ||
450 | } | ||
451 | else | ||
452 | { | ||
453 | int lockcmd; | ||
454 | |||
455 | switch (rqdata.cmd) | ||
456 | { | ||
457 | case NCP_LOCK_EX: lockcmd=1; break; | ||
458 | case NCP_LOCK_SH: lockcmd=3; break; | ||
459 | default: lockcmd=0; break; | ||
460 | } | ||
461 | result = ncp_LogPhysicalRecord(NCP_SERVER(inode), | ||
462 | NCP_FINFO(inode)->file_handle, | ||
463 | lockcmd, | ||
464 | rqdata.offset, | ||
465 | rqdata.length, | ||
466 | rqdata.timeout); | ||
467 | if (result > 0) result = -EAGAIN; | ||
468 | } | ||
469 | outrel: | ||
470 | ncp_inode_close(inode); | ||
471 | return result; | ||
472 | } | ||
473 | #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ | ||
474 | |||
475 | case NCP_IOC_GETOBJECTNAME: | ||
476 | if (current->uid != server->m.mounted_uid) { | ||
477 | return -EACCES; | ||
478 | } | ||
479 | { | ||
480 | struct ncp_objectname_ioctl user; | ||
481 | size_t outl; | ||
482 | |||
483 | if (copy_from_user(&user, argp, sizeof(user))) | ||
484 | return -EFAULT; | ||
485 | user.auth_type = server->auth.auth_type; | ||
486 | outl = user.object_name_len; | ||
487 | user.object_name_len = server->auth.object_name_len; | ||
488 | if (outl > user.object_name_len) | ||
489 | outl = user.object_name_len; | ||
490 | if (outl) { | ||
491 | if (copy_to_user(user.object_name, | ||
492 | server->auth.object_name, | ||
493 | outl)) return -EFAULT; | ||
494 | } | ||
495 | if (copy_to_user(argp, &user, sizeof(user))) | ||
496 | return -EFAULT; | ||
497 | return 0; | ||
498 | } | ||
499 | case NCP_IOC_SETOBJECTNAME: | ||
500 | if (current->uid != server->m.mounted_uid) { | ||
501 | return -EACCES; | ||
502 | } | ||
503 | { | ||
504 | struct ncp_objectname_ioctl user; | ||
505 | void* newname; | ||
506 | void* oldname; | ||
507 | size_t oldnamelen; | ||
508 | void* oldprivate; | ||
509 | size_t oldprivatelen; | ||
510 | |||
511 | if (copy_from_user(&user, argp, sizeof(user))) | ||
512 | return -EFAULT; | ||
513 | if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN) | ||
514 | return -ENOMEM; | ||
515 | if (user.object_name_len) { | ||
516 | newname = ncp_kmalloc(user.object_name_len, GFP_USER); | ||
517 | if (!newname) return -ENOMEM; | ||
518 | if (copy_from_user(newname, user.object_name, user.object_name_len)) { | ||
519 | ncp_kfree_s(newname, user.object_name_len); | ||
520 | return -EFAULT; | ||
521 | } | ||
522 | } else { | ||
523 | newname = NULL; | ||
524 | } | ||
525 | /* enter critical section */ | ||
526 | /* maybe that kfree can sleep so do that this way */ | ||
527 | /* it is at least more SMP friendly (in future...) */ | ||
528 | oldname = server->auth.object_name; | ||
529 | oldnamelen = server->auth.object_name_len; | ||
530 | oldprivate = server->priv.data; | ||
531 | oldprivatelen = server->priv.len; | ||
532 | server->auth.auth_type = user.auth_type; | ||
533 | server->auth.object_name_len = user.object_name_len; | ||
534 | server->auth.object_name = newname; | ||
535 | server->priv.len = 0; | ||
536 | server->priv.data = NULL; | ||
537 | /* leave critical section */ | ||
538 | if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen); | ||
539 | if (oldname) ncp_kfree_s(oldname, oldnamelen); | ||
540 | return 0; | ||
541 | } | ||
542 | case NCP_IOC_GETPRIVATEDATA: | ||
543 | if (current->uid != server->m.mounted_uid) { | ||
544 | return -EACCES; | ||
545 | } | ||
546 | { | ||
547 | struct ncp_privatedata_ioctl user; | ||
548 | size_t outl; | ||
549 | |||
550 | if (copy_from_user(&user, argp, sizeof(user))) | ||
551 | return -EFAULT; | ||
552 | outl = user.len; | ||
553 | user.len = server->priv.len; | ||
554 | if (outl > user.len) outl = user.len; | ||
555 | if (outl) { | ||
556 | if (copy_to_user(user.data, | ||
557 | server->priv.data, | ||
558 | outl)) return -EFAULT; | ||
559 | } | ||
560 | if (copy_to_user(argp, &user, sizeof(user))) | ||
561 | return -EFAULT; | ||
562 | return 0; | ||
563 | } | ||
564 | case NCP_IOC_SETPRIVATEDATA: | ||
565 | if (current->uid != server->m.mounted_uid) { | ||
566 | return -EACCES; | ||
567 | } | ||
568 | { | ||
569 | struct ncp_privatedata_ioctl user; | ||
570 | void* new; | ||
571 | void* old; | ||
572 | size_t oldlen; | ||
573 | |||
574 | if (copy_from_user(&user, argp, sizeof(user))) | ||
575 | return -EFAULT; | ||
576 | if (user.len > NCP_PRIVATE_DATA_MAX_LEN) | ||
577 | return -ENOMEM; | ||
578 | if (user.len) { | ||
579 | new = ncp_kmalloc(user.len, GFP_USER); | ||
580 | if (!new) return -ENOMEM; | ||
581 | if (copy_from_user(new, user.data, user.len)) { | ||
582 | ncp_kfree_s(new, user.len); | ||
583 | return -EFAULT; | ||
584 | } | ||
585 | } else { | ||
586 | new = NULL; | ||
587 | } | ||
588 | /* enter critical section */ | ||
589 | old = server->priv.data; | ||
590 | oldlen = server->priv.len; | ||
591 | server->priv.len = user.len; | ||
592 | server->priv.data = new; | ||
593 | /* leave critical section */ | ||
594 | if (old) ncp_kfree_s(old, oldlen); | ||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | #ifdef CONFIG_NCPFS_NLS | ||
599 | case NCP_IOC_SETCHARSETS: | ||
600 | return ncp_set_charsets(server, argp); | ||
601 | |||
602 | case NCP_IOC_GETCHARSETS: | ||
603 | return ncp_get_charsets(server, argp); | ||
604 | |||
605 | #endif /* CONFIG_NCPFS_NLS */ | ||
606 | |||
607 | case NCP_IOC_SETDENTRYTTL: | ||
608 | if ((permission(inode, MAY_WRITE, NULL) != 0) && | ||
609 | (current->uid != server->m.mounted_uid)) | ||
610 | return -EACCES; | ||
611 | { | ||
612 | u_int32_t user; | ||
613 | |||
614 | if (copy_from_user(&user, argp, sizeof(user))) | ||
615 | return -EFAULT; | ||
616 | /* 20 secs at most... */ | ||
617 | if (user > 20000) | ||
618 | return -EINVAL; | ||
619 | user = (user * HZ) / 1000; | ||
620 | server->dentry_ttl = user; | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | case NCP_IOC_GETDENTRYTTL: | ||
625 | { | ||
626 | u_int32_t user = (server->dentry_ttl * 1000) / HZ; | ||
627 | if (copy_to_user(argp, &user, sizeof(user))) | ||
628 | return -EFAULT; | ||
629 | return 0; | ||
630 | } | ||
631 | |||
632 | } | ||
633 | /* #ifdef CONFIG_UID16 */ | ||
634 | /* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2, | ||
635 | so we have this out of switch */ | ||
636 | if (cmd == NCP_IOC_GETMOUNTUID) { | ||
637 | __kernel_uid_t uid = 0; | ||
638 | if ((permission(inode, MAY_READ, NULL) != 0) | ||
639 | && (current->uid != server->m.mounted_uid)) { | ||
640 | return -EACCES; | ||
641 | } | ||
642 | SET_UID(uid, server->m.mounted_uid); | ||
643 | if (put_user(uid, (__kernel_uid_t __user *)argp)) | ||
644 | return -EFAULT; | ||
645 | return 0; | ||
646 | } | ||
647 | /* #endif */ | ||
648 | return -EINVAL; | ||
649 | } | ||
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c new file mode 100644 index 000000000000..52d60c3d8996 --- /dev/null +++ b/fs/ncpfs/mmap.c | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * mmap.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 by Volker Lendecke | ||
5 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include <linux/stat.h> | ||
10 | #include <linux/time.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/shm.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/mman.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/fcntl.h> | ||
19 | #include <linux/ncp_fs.h> | ||
20 | |||
21 | #include "ncplib_kernel.h" | ||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/system.h> | ||
24 | |||
25 | /* | ||
26 | * Fill in the supplied page for mmap | ||
27 | */ | ||
28 | static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area, | ||
29 | unsigned long address, int *type) | ||
30 | { | ||
31 | struct file *file = area->vm_file; | ||
32 | struct dentry *dentry = file->f_dentry; | ||
33 | struct inode *inode = dentry->d_inode; | ||
34 | struct page* page; | ||
35 | char *pg_addr; | ||
36 | unsigned int already_read; | ||
37 | unsigned int count; | ||
38 | int bufsize; | ||
39 | int pos; | ||
40 | |||
41 | page = alloc_page(GFP_HIGHUSER); /* ncpfs has nothing against high pages | ||
42 | as long as recvmsg and memset works on it */ | ||
43 | if (!page) | ||
44 | return page; | ||
45 | pg_addr = kmap(page); | ||
46 | address &= PAGE_MASK; | ||
47 | pos = address - area->vm_start + (area->vm_pgoff << PAGE_SHIFT); | ||
48 | |||
49 | count = PAGE_SIZE; | ||
50 | if (address + PAGE_SIZE > area->vm_end) { | ||
51 | count = area->vm_end - address; | ||
52 | } | ||
53 | /* what we can read in one go */ | ||
54 | bufsize = NCP_SERVER(inode)->buffer_size; | ||
55 | |||
56 | already_read = 0; | ||
57 | if (ncp_make_open(inode, O_RDONLY) >= 0) { | ||
58 | while (already_read < count) { | ||
59 | int read_this_time; | ||
60 | int to_read; | ||
61 | |||
62 | to_read = bufsize - (pos % bufsize); | ||
63 | |||
64 | to_read = min_t(unsigned int, to_read, count - already_read); | ||
65 | |||
66 | if (ncp_read_kernel(NCP_SERVER(inode), | ||
67 | NCP_FINFO(inode)->file_handle, | ||
68 | pos, to_read, | ||
69 | pg_addr + already_read, | ||
70 | &read_this_time) != 0) { | ||
71 | read_this_time = 0; | ||
72 | } | ||
73 | pos += read_this_time; | ||
74 | already_read += read_this_time; | ||
75 | |||
76 | if (read_this_time < to_read) { | ||
77 | break; | ||
78 | } | ||
79 | } | ||
80 | ncp_inode_close(inode); | ||
81 | |||
82 | } | ||
83 | |||
84 | if (already_read < PAGE_SIZE) | ||
85 | memset(pg_addr + already_read, 0, PAGE_SIZE - already_read); | ||
86 | flush_dcache_page(page); | ||
87 | kunmap(page); | ||
88 | |||
89 | /* | ||
90 | * If I understand ncp_read_kernel() properly, the above always | ||
91 | * fetches from the network, here the analogue of disk. | ||
92 | * -- wli | ||
93 | */ | ||
94 | if (type) | ||
95 | *type = VM_FAULT_MAJOR; | ||
96 | inc_page_state(pgmajfault); | ||
97 | return page; | ||
98 | } | ||
99 | |||
100 | static struct vm_operations_struct ncp_file_mmap = | ||
101 | { | ||
102 | .nopage = ncp_file_mmap_nopage, | ||
103 | }; | ||
104 | |||
105 | |||
106 | /* This is used for a general mmap of a ncp file */ | ||
107 | int ncp_mmap(struct file *file, struct vm_area_struct *vma) | ||
108 | { | ||
109 | struct inode *inode = file->f_dentry->d_inode; | ||
110 | |||
111 | DPRINTK("ncp_mmap: called\n"); | ||
112 | |||
113 | if (!ncp_conn_valid(NCP_SERVER(inode))) | ||
114 | return -EIO; | ||
115 | |||
116 | /* only PAGE_COW or read-only supported now */ | ||
117 | if (vma->vm_flags & VM_SHARED) | ||
118 | return -EINVAL; | ||
119 | /* we do not support files bigger than 4GB... We eventually | ||
120 | supports just 4GB... */ | ||
121 | if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff | ||
122 | > (1U << (32 - PAGE_SHIFT))) | ||
123 | return -EFBIG; | ||
124 | |||
125 | vma->vm_ops = &ncp_file_mmap; | ||
126 | file_accessed(file); | ||
127 | return 0; | ||
128 | } | ||
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c new file mode 100644 index 000000000000..e4eb5ed4bee4 --- /dev/null +++ b/fs/ncpfs/ncplib_kernel.c | |||
@@ -0,0 +1,1355 @@ | |||
1 | /* | ||
2 | * ncplib_kernel.c | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 by Volker Lendecke | ||
5 | * Modified for big endian by J.F. Chadima and David S. Miller | ||
6 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
7 | * Modified 1999 Wolfram Pienkoss for NLS | ||
8 | * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | |||
13 | #include <linux/config.h> | ||
14 | |||
15 | #include "ncplib_kernel.h" | ||
16 | |||
17 | static inline void assert_server_locked(struct ncp_server *server) | ||
18 | { | ||
19 | if (server->lock == 0) { | ||
20 | DPRINTK("ncpfs: server not locked!\n"); | ||
21 | } | ||
22 | } | ||
23 | |||
24 | static void ncp_add_byte(struct ncp_server *server, __u8 x) | ||
25 | { | ||
26 | assert_server_locked(server); | ||
27 | *(__u8 *) (&(server->packet[server->current_size])) = x; | ||
28 | server->current_size += 1; | ||
29 | return; | ||
30 | } | ||
31 | |||
32 | static void ncp_add_word(struct ncp_server *server, __le16 x) | ||
33 | { | ||
34 | assert_server_locked(server); | ||
35 | put_unaligned(x, (__le16 *) (&(server->packet[server->current_size]))); | ||
36 | server->current_size += 2; | ||
37 | return; | ||
38 | } | ||
39 | |||
40 | static void ncp_add_be16(struct ncp_server *server, __u16 x) | ||
41 | { | ||
42 | assert_server_locked(server); | ||
43 | put_unaligned(cpu_to_be16(x), (__be16 *) (&(server->packet[server->current_size]))); | ||
44 | server->current_size += 2; | ||
45 | } | ||
46 | |||
47 | static void ncp_add_dword(struct ncp_server *server, __le32 x) | ||
48 | { | ||
49 | assert_server_locked(server); | ||
50 | put_unaligned(x, (__le32 *) (&(server->packet[server->current_size]))); | ||
51 | server->current_size += 4; | ||
52 | return; | ||
53 | } | ||
54 | |||
55 | static void ncp_add_be32(struct ncp_server *server, __u32 x) | ||
56 | { | ||
57 | assert_server_locked(server); | ||
58 | put_unaligned(cpu_to_be32(x), (__be32 *)(&(server->packet[server->current_size]))); | ||
59 | server->current_size += 4; | ||
60 | } | ||
61 | |||
62 | static inline void ncp_add_dword_lh(struct ncp_server *server, __u32 x) { | ||
63 | ncp_add_dword(server, cpu_to_le32(x)); | ||
64 | } | ||
65 | |||
66 | static void ncp_add_mem(struct ncp_server *server, const void *source, int size) | ||
67 | { | ||
68 | assert_server_locked(server); | ||
69 | memcpy(&(server->packet[server->current_size]), source, size); | ||
70 | server->current_size += size; | ||
71 | return; | ||
72 | } | ||
73 | |||
74 | static void ncp_add_pstring(struct ncp_server *server, const char *s) | ||
75 | { | ||
76 | int len = strlen(s); | ||
77 | assert_server_locked(server); | ||
78 | if (len > 255) { | ||
79 | DPRINTK("ncpfs: string too long: %s\n", s); | ||
80 | len = 255; | ||
81 | } | ||
82 | ncp_add_byte(server, len); | ||
83 | ncp_add_mem(server, s, len); | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | static inline void ncp_init_request(struct ncp_server *server) | ||
88 | { | ||
89 | ncp_lock_server(server); | ||
90 | |||
91 | server->current_size = sizeof(struct ncp_request_header); | ||
92 | server->has_subfunction = 0; | ||
93 | } | ||
94 | |||
95 | static inline void ncp_init_request_s(struct ncp_server *server, int subfunction) | ||
96 | { | ||
97 | ncp_lock_server(server); | ||
98 | |||
99 | server->current_size = sizeof(struct ncp_request_header) + 2; | ||
100 | ncp_add_byte(server, subfunction); | ||
101 | |||
102 | server->has_subfunction = 1; | ||
103 | } | ||
104 | |||
105 | static inline char * | ||
106 | ncp_reply_data(struct ncp_server *server, int offset) | ||
107 | { | ||
108 | return &(server->packet[sizeof(struct ncp_reply_header) + offset]); | ||
109 | } | ||
110 | |||
111 | static inline __u8 BVAL(void* data) | ||
112 | { | ||
113 | return get_unaligned((__u8*)data); | ||
114 | } | ||
115 | |||
116 | static __u8 | ||
117 | ncp_reply_byte(struct ncp_server *server, int offset) | ||
118 | { | ||
119 | return get_unaligned((__u8 *) ncp_reply_data(server, offset)); | ||
120 | } | ||
121 | |||
122 | static inline __u16 WVAL_LH(void* data) | ||
123 | { | ||
124 | return le16_to_cpu(get_unaligned((__le16*)data)); | ||
125 | } | ||
126 | |||
127 | static __u16 | ||
128 | ncp_reply_le16(struct ncp_server *server, int offset) | ||
129 | { | ||
130 | return le16_to_cpu(get_unaligned((__le16 *) ncp_reply_data(server, offset))); | ||
131 | } | ||
132 | |||
133 | static __u16 | ||
134 | ncp_reply_be16(struct ncp_server *server, int offset) | ||
135 | { | ||
136 | return be16_to_cpu(get_unaligned((__be16 *) ncp_reply_data(server, offset))); | ||
137 | } | ||
138 | |||
139 | static inline __u32 DVAL_LH(void* data) | ||
140 | { | ||
141 | return le32_to_cpu(get_unaligned((__le32*)data)); | ||
142 | } | ||
143 | |||
144 | static __le32 | ||
145 | ncp_reply_dword(struct ncp_server *server, int offset) | ||
146 | { | ||
147 | return get_unaligned((__le32 *) ncp_reply_data(server, offset)); | ||
148 | } | ||
149 | |||
150 | static inline __u32 ncp_reply_dword_lh(struct ncp_server* server, int offset) { | ||
151 | return le32_to_cpu(ncp_reply_dword(server, offset)); | ||
152 | } | ||
153 | |||
154 | int | ||
155 | ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target) | ||
156 | { | ||
157 | int result; | ||
158 | |||
159 | ncp_init_request(server); | ||
160 | ncp_add_be16(server, size); | ||
161 | |||
162 | if ((result = ncp_request(server, 33)) != 0) { | ||
163 | ncp_unlock_server(server); | ||
164 | return result; | ||
165 | } | ||
166 | *target = min_t(unsigned int, ncp_reply_be16(server, 0), size); | ||
167 | |||
168 | ncp_unlock_server(server); | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | |||
173 | /* options: | ||
174 | * bit 0 ipx checksum | ||
175 | * bit 1 packet signing | ||
176 | */ | ||
177 | int | ||
178 | ncp_negotiate_size_and_options(struct ncp_server *server, | ||
179 | int size, int options, int *ret_size, int *ret_options) { | ||
180 | int result; | ||
181 | |||
182 | /* there is minimum */ | ||
183 | if (size < NCP_BLOCK_SIZE) size = NCP_BLOCK_SIZE; | ||
184 | |||
185 | ncp_init_request(server); | ||
186 | ncp_add_be16(server, size); | ||
187 | ncp_add_byte(server, options); | ||
188 | |||
189 | if ((result = ncp_request(server, 0x61)) != 0) | ||
190 | { | ||
191 | ncp_unlock_server(server); | ||
192 | return result; | ||
193 | } | ||
194 | |||
195 | /* NCP over UDP returns 0 (!!!) */ | ||
196 | result = ncp_reply_be16(server, 0); | ||
197 | if (result >= NCP_BLOCK_SIZE) | ||
198 | size = min(result, size); | ||
199 | *ret_size = size; | ||
200 | *ret_options = ncp_reply_byte(server, 4); | ||
201 | |||
202 | ncp_unlock_server(server); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | int ncp_get_volume_info_with_number(struct ncp_server* server, | ||
207 | int n, struct ncp_volume_info* target) { | ||
208 | int result; | ||
209 | int len; | ||
210 | |||
211 | ncp_init_request_s(server, 44); | ||
212 | ncp_add_byte(server, n); | ||
213 | |||
214 | if ((result = ncp_request(server, 22)) != 0) { | ||
215 | goto out; | ||
216 | } | ||
217 | target->total_blocks = ncp_reply_dword_lh(server, 0); | ||
218 | target->free_blocks = ncp_reply_dword_lh(server, 4); | ||
219 | target->purgeable_blocks = ncp_reply_dword_lh(server, 8); | ||
220 | target->not_yet_purgeable_blocks = ncp_reply_dword_lh(server, 12); | ||
221 | target->total_dir_entries = ncp_reply_dword_lh(server, 16); | ||
222 | target->available_dir_entries = ncp_reply_dword_lh(server, 20); | ||
223 | target->sectors_per_block = ncp_reply_byte(server, 28); | ||
224 | |||
225 | memset(&(target->volume_name), 0, sizeof(target->volume_name)); | ||
226 | |||
227 | result = -EIO; | ||
228 | len = ncp_reply_byte(server, 29); | ||
229 | if (len > NCP_VOLNAME_LEN) { | ||
230 | DPRINTK("ncpfs: volume name too long: %d\n", len); | ||
231 | goto out; | ||
232 | } | ||
233 | memcpy(&(target->volume_name), ncp_reply_data(server, 30), len); | ||
234 | result = 0; | ||
235 | out: | ||
236 | ncp_unlock_server(server); | ||
237 | return result; | ||
238 | } | ||
239 | |||
240 | int ncp_get_directory_info(struct ncp_server* server, __u8 n, | ||
241 | struct ncp_volume_info* target) { | ||
242 | int result; | ||
243 | int len; | ||
244 | |||
245 | ncp_init_request_s(server, 45); | ||
246 | ncp_add_byte(server, n); | ||
247 | |||
248 | if ((result = ncp_request(server, 22)) != 0) { | ||
249 | goto out; | ||
250 | } | ||
251 | target->total_blocks = ncp_reply_dword_lh(server, 0); | ||
252 | target->free_blocks = ncp_reply_dword_lh(server, 4); | ||
253 | target->purgeable_blocks = 0; | ||
254 | target->not_yet_purgeable_blocks = 0; | ||
255 | target->total_dir_entries = ncp_reply_dword_lh(server, 8); | ||
256 | target->available_dir_entries = ncp_reply_dword_lh(server, 12); | ||
257 | target->sectors_per_block = ncp_reply_byte(server, 20); | ||
258 | |||
259 | memset(&(target->volume_name), 0, sizeof(target->volume_name)); | ||
260 | |||
261 | result = -EIO; | ||
262 | len = ncp_reply_byte(server, 21); | ||
263 | if (len > NCP_VOLNAME_LEN) { | ||
264 | DPRINTK("ncpfs: volume name too long: %d\n", len); | ||
265 | goto out; | ||
266 | } | ||
267 | memcpy(&(target->volume_name), ncp_reply_data(server, 22), len); | ||
268 | result = 0; | ||
269 | out: | ||
270 | ncp_unlock_server(server); | ||
271 | return result; | ||
272 | } | ||
273 | |||
274 | int | ||
275 | ncp_close_file(struct ncp_server *server, const char *file_id) | ||
276 | { | ||
277 | int result; | ||
278 | |||
279 | ncp_init_request(server); | ||
280 | ncp_add_byte(server, 0); | ||
281 | ncp_add_mem(server, file_id, 6); | ||
282 | |||
283 | result = ncp_request(server, 66); | ||
284 | ncp_unlock_server(server); | ||
285 | return result; | ||
286 | } | ||
287 | |||
288 | int | ||
289 | ncp_make_closed(struct inode *inode) | ||
290 | { | ||
291 | int err; | ||
292 | |||
293 | err = 0; | ||
294 | down(&NCP_FINFO(inode)->open_sem); | ||
295 | if (atomic_read(&NCP_FINFO(inode)->opened) == 1) { | ||
296 | atomic_set(&NCP_FINFO(inode)->opened, 0); | ||
297 | err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); | ||
298 | |||
299 | if (!err) | ||
300 | PPRINTK("ncp_make_closed: volnum=%d, dirent=%u, error=%d\n", | ||
301 | NCP_FINFO(inode)->volNumber, | ||
302 | NCP_FINFO(inode)->dirEntNum, err); | ||
303 | } | ||
304 | up(&NCP_FINFO(inode)->open_sem); | ||
305 | return err; | ||
306 | } | ||
307 | |||
308 | static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num, | ||
309 | __le32 dir_base, int have_dir_base, | ||
310 | const char *path) | ||
311 | { | ||
312 | ncp_add_byte(server, vol_num); | ||
313 | ncp_add_dword(server, dir_base); | ||
314 | if (have_dir_base != 0) { | ||
315 | ncp_add_byte(server, 1); /* dir_base */ | ||
316 | } else { | ||
317 | ncp_add_byte(server, 0xff); /* no handle */ | ||
318 | } | ||
319 | if (path != NULL) { | ||
320 | ncp_add_byte(server, 1); /* 1 component */ | ||
321 | ncp_add_pstring(server, path); | ||
322 | } else { | ||
323 | ncp_add_byte(server, 0); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | int ncp_dirhandle_alloc(struct ncp_server* server, __u8 volnum, __le32 dirent, | ||
328 | __u8* dirhandle) { | ||
329 | int result; | ||
330 | |||
331 | ncp_init_request(server); | ||
332 | ncp_add_byte(server, 12); /* subfunction */ | ||
333 | ncp_add_byte(server, NW_NS_DOS); | ||
334 | ncp_add_byte(server, 0); | ||
335 | ncp_add_word(server, 0); | ||
336 | ncp_add_handle_path(server, volnum, dirent, 1, NULL); | ||
337 | if ((result = ncp_request(server, 87)) == 0) { | ||
338 | *dirhandle = ncp_reply_byte(server, 0); | ||
339 | } | ||
340 | ncp_unlock_server(server); | ||
341 | return result; | ||
342 | } | ||
343 | |||
344 | int ncp_dirhandle_free(struct ncp_server* server, __u8 dirhandle) { | ||
345 | int result; | ||
346 | |||
347 | ncp_init_request_s(server, 20); | ||
348 | ncp_add_byte(server, dirhandle); | ||
349 | result = ncp_request(server, 22); | ||
350 | ncp_unlock_server(server); | ||
351 | return result; | ||
352 | } | ||
353 | |||
354 | void ncp_extract_file_info(void *structure, struct nw_info_struct *target) | ||
355 | { | ||
356 | __u8 *name_len; | ||
357 | const int info_struct_size = offsetof(struct nw_info_struct, nameLen); | ||
358 | |||
359 | memcpy(target, structure, info_struct_size); | ||
360 | name_len = structure + info_struct_size; | ||
361 | target->nameLen = *name_len; | ||
362 | memcpy(target->entryName, name_len + 1, *name_len); | ||
363 | target->entryName[*name_len] = '\0'; | ||
364 | target->volNumber = le32_to_cpu(target->volNumber); | ||
365 | return; | ||
366 | } | ||
367 | |||
368 | #ifdef CONFIG_NCPFS_NFS_NS | ||
369 | static inline void ncp_extract_nfs_info(unsigned char *structure, | ||
370 | struct nw_nfs_info *target) | ||
371 | { | ||
372 | target->mode = DVAL_LH(structure); | ||
373 | target->rdev = DVAL_LH(structure + 8); | ||
374 | } | ||
375 | #endif | ||
376 | |||
377 | int ncp_obtain_nfs_info(struct ncp_server *server, | ||
378 | struct nw_info_struct *target) | ||
379 | |||
380 | { | ||
381 | int result = 0; | ||
382 | #ifdef CONFIG_NCPFS_NFS_NS | ||
383 | __u32 volnum = target->volNumber; | ||
384 | |||
385 | if (ncp_is_nfs_extras(server, volnum)) { | ||
386 | ncp_init_request(server); | ||
387 | ncp_add_byte(server, 19); /* subfunction */ | ||
388 | ncp_add_byte(server, server->name_space[volnum]); | ||
389 | ncp_add_byte(server, NW_NS_NFS); | ||
390 | ncp_add_byte(server, 0); | ||
391 | ncp_add_byte(server, volnum); | ||
392 | ncp_add_dword(server, target->dirEntNum); | ||
393 | /* We must retrieve both nlinks and rdev, otherwise some server versions | ||
394 | report zeroes instead of valid data */ | ||
395 | ncp_add_dword_lh(server, NSIBM_NFS_MODE | NSIBM_NFS_NLINKS | NSIBM_NFS_RDEV); | ||
396 | |||
397 | if ((result = ncp_request(server, 87)) == 0) { | ||
398 | ncp_extract_nfs_info(ncp_reply_data(server, 0), &target->nfs); | ||
399 | DPRINTK(KERN_DEBUG | ||
400 | "ncp_obtain_nfs_info: (%s) mode=0%o, rdev=0x%x\n", | ||
401 | target->entryName, target->nfs.mode, | ||
402 | target->nfs.rdev); | ||
403 | } else { | ||
404 | target->nfs.mode = 0; | ||
405 | target->nfs.rdev = 0; | ||
406 | } | ||
407 | ncp_unlock_server(server); | ||
408 | |||
409 | } else | ||
410 | #endif | ||
411 | { | ||
412 | target->nfs.mode = 0; | ||
413 | target->nfs.rdev = 0; | ||
414 | } | ||
415 | return result; | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | * Returns information for a (one-component) name relative to | ||
420 | * the specified directory. | ||
421 | */ | ||
422 | int ncp_obtain_info(struct ncp_server *server, struct inode *dir, char *path, | ||
423 | struct nw_info_struct *target) | ||
424 | { | ||
425 | __u8 volnum = NCP_FINFO(dir)->volNumber; | ||
426 | __le32 dirent = NCP_FINFO(dir)->dirEntNum; | ||
427 | int result; | ||
428 | |||
429 | if (target == NULL) { | ||
430 | printk(KERN_ERR "ncp_obtain_info: invalid call\n"); | ||
431 | return -EINVAL; | ||
432 | } | ||
433 | ncp_init_request(server); | ||
434 | ncp_add_byte(server, 6); /* subfunction */ | ||
435 | ncp_add_byte(server, server->name_space[volnum]); | ||
436 | ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */ | ||
437 | ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */ | ||
438 | ncp_add_dword(server, RIM_ALL); | ||
439 | ncp_add_handle_path(server, volnum, dirent, 1, path); | ||
440 | |||
441 | if ((result = ncp_request(server, 87)) != 0) | ||
442 | goto out; | ||
443 | ncp_extract_file_info(ncp_reply_data(server, 0), target); | ||
444 | ncp_unlock_server(server); | ||
445 | |||
446 | result = ncp_obtain_nfs_info(server, target); | ||
447 | return result; | ||
448 | |||
449 | out: | ||
450 | ncp_unlock_server(server); | ||
451 | return result; | ||
452 | } | ||
453 | |||
454 | #ifdef CONFIG_NCPFS_NFS_NS | ||
455 | static int | ||
456 | ncp_obtain_DOS_dir_base(struct ncp_server *server, | ||
457 | __u8 volnum, __le32 dirent, | ||
458 | char *path, /* At most 1 component */ | ||
459 | __le32 *DOS_dir_base) | ||
460 | { | ||
461 | int result; | ||
462 | |||
463 | ncp_init_request(server); | ||
464 | ncp_add_byte(server, 6); /* subfunction */ | ||
465 | ncp_add_byte(server, server->name_space[volnum]); | ||
466 | ncp_add_byte(server, server->name_space[volnum]); | ||
467 | ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */ | ||
468 | ncp_add_dword(server, RIM_DIRECTORY); | ||
469 | ncp_add_handle_path(server, volnum, dirent, 1, path); | ||
470 | |||
471 | if ((result = ncp_request(server, 87)) == 0) | ||
472 | { | ||
473 | if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34); | ||
474 | } | ||
475 | ncp_unlock_server(server); | ||
476 | return result; | ||
477 | } | ||
478 | #endif /* CONFIG_NCPFS_NFS_NS */ | ||
479 | |||
480 | static inline int | ||
481 | ncp_get_known_namespace(struct ncp_server *server, __u8 volume) | ||
482 | { | ||
483 | #if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) | ||
484 | int result; | ||
485 | __u8 *namespace; | ||
486 | __u16 no_namespaces; | ||
487 | |||
488 | ncp_init_request(server); | ||
489 | ncp_add_byte(server, 24); /* Subfunction: Get Name Spaces Loaded */ | ||
490 | ncp_add_word(server, 0); | ||
491 | ncp_add_byte(server, volume); | ||
492 | |||
493 | if ((result = ncp_request(server, 87)) != 0) { | ||
494 | ncp_unlock_server(server); | ||
495 | return NW_NS_DOS; /* not result ?? */ | ||
496 | } | ||
497 | |||
498 | result = NW_NS_DOS; | ||
499 | no_namespaces = ncp_reply_le16(server, 0); | ||
500 | namespace = ncp_reply_data(server, 2); | ||
501 | |||
502 | while (no_namespaces > 0) { | ||
503 | DPRINTK("get_namespaces: found %d on %d\n", *namespace, volume); | ||
504 | |||
505 | #ifdef CONFIG_NCPFS_NFS_NS | ||
506 | if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS)) | ||
507 | { | ||
508 | result = NW_NS_NFS; | ||
509 | break; | ||
510 | } | ||
511 | #endif /* CONFIG_NCPFS_NFS_NS */ | ||
512 | #ifdef CONFIG_NCPFS_OS2_NS | ||
513 | if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2)) | ||
514 | { | ||
515 | result = NW_NS_OS2; | ||
516 | } | ||
517 | #endif /* CONFIG_NCPFS_OS2_NS */ | ||
518 | namespace += 1; | ||
519 | no_namespaces -= 1; | ||
520 | } | ||
521 | ncp_unlock_server(server); | ||
522 | return result; | ||
523 | #else /* neither OS2 nor NFS - only DOS */ | ||
524 | return NW_NS_DOS; | ||
525 | #endif /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */ | ||
526 | } | ||
527 | |||
528 | static int | ||
529 | ncp_ObtainSpecificDirBase(struct ncp_server *server, | ||
530 | __u8 nsSrc, __u8 nsDst, __u8 vol_num, __le32 dir_base, | ||
531 | char *path, /* At most 1 component */ | ||
532 | __le32 *dirEntNum, __le32 *DosDirNum) | ||
533 | { | ||
534 | int result; | ||
535 | |||
536 | ncp_init_request(server); | ||
537 | ncp_add_byte(server, 6); /* subfunction */ | ||
538 | ncp_add_byte(server, nsSrc); | ||
539 | ncp_add_byte(server, nsDst); | ||
540 | ncp_add_word(server, cpu_to_le16(0x8006)); /* get all */ | ||
541 | ncp_add_dword(server, RIM_ALL); | ||
542 | ncp_add_handle_path(server, vol_num, dir_base, 1, path); | ||
543 | |||
544 | if ((result = ncp_request(server, 87)) != 0) | ||
545 | { | ||
546 | ncp_unlock_server(server); | ||
547 | return result; | ||
548 | } | ||
549 | |||
550 | if (dirEntNum) | ||
551 | *dirEntNum = ncp_reply_dword(server, 0x30); | ||
552 | if (DosDirNum) | ||
553 | *DosDirNum = ncp_reply_dword(server, 0x34); | ||
554 | ncp_unlock_server(server); | ||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | int | ||
559 | ncp_mount_subdir(struct ncp_server *server, | ||
560 | __u8 volNumber, __u8 srcNS, __le32 dirEntNum, | ||
561 | __u32* volume, __le32* newDirEnt, __le32* newDosEnt) | ||
562 | { | ||
563 | int dstNS; | ||
564 | int result; | ||
565 | |||
566 | dstNS = ncp_get_known_namespace(server, volNumber); | ||
567 | if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber, | ||
568 | dirEntNum, NULL, newDirEnt, newDosEnt)) != 0) | ||
569 | { | ||
570 | return result; | ||
571 | } | ||
572 | server->name_space[volNumber] = dstNS; | ||
573 | *volume = volNumber; | ||
574 | server->m.mounted_vol[1] = 0; | ||
575 | server->m.mounted_vol[0] = 'X'; | ||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | int | ||
580 | ncp_get_volume_root(struct ncp_server *server, const char *volname, | ||
581 | __u32* volume, __le32* dirent, __le32* dosdirent) | ||
582 | { | ||
583 | int result; | ||
584 | __u8 volnum; | ||
585 | |||
586 | DPRINTK("ncp_get_volume_root: looking up vol %s\n", volname); | ||
587 | |||
588 | ncp_init_request(server); | ||
589 | ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */ | ||
590 | ncp_add_byte(server, 0); /* DOS namespace */ | ||
591 | ncp_add_byte(server, 0); /* reserved */ | ||
592 | ncp_add_byte(server, 0); /* reserved */ | ||
593 | ncp_add_byte(server, 0); /* reserved */ | ||
594 | |||
595 | ncp_add_byte(server, 0); /* faked volume number */ | ||
596 | ncp_add_dword(server, 0); /* faked dir_base */ | ||
597 | ncp_add_byte(server, 0xff); /* Don't have a dir_base */ | ||
598 | ncp_add_byte(server, 1); /* 1 path component */ | ||
599 | ncp_add_pstring(server, volname); | ||
600 | |||
601 | if ((result = ncp_request(server, 87)) != 0) { | ||
602 | ncp_unlock_server(server); | ||
603 | return result; | ||
604 | } | ||
605 | *dirent = *dosdirent = ncp_reply_dword(server, 4); | ||
606 | volnum = ncp_reply_byte(server, 8); | ||
607 | ncp_unlock_server(server); | ||
608 | *volume = volnum; | ||
609 | |||
610 | server->name_space[volnum] = ncp_get_known_namespace(server, volnum); | ||
611 | |||
612 | DPRINTK("lookup_vol: namespace[%d] = %d\n", | ||
613 | volnum, server->name_space[volnum]); | ||
614 | |||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | int | ||
619 | ncp_lookup_volume(struct ncp_server *server, const char *volname, | ||
620 | struct nw_info_struct *target) | ||
621 | { | ||
622 | int result; | ||
623 | |||
624 | memset(target, 0, sizeof(*target)); | ||
625 | result = ncp_get_volume_root(server, volname, | ||
626 | &target->volNumber, &target->dirEntNum, &target->DosDirNum); | ||
627 | if (result) { | ||
628 | return result; | ||
629 | } | ||
630 | target->nameLen = strlen(volname); | ||
631 | memcpy(target->entryName, volname, target->nameLen+1); | ||
632 | target->attributes = aDIR; | ||
633 | /* set dates to Jan 1, 1986 00:00 */ | ||
634 | target->creationTime = target->modifyTime = cpu_to_le16(0x0000); | ||
635 | target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21); | ||
636 | target->nfs.mode = 0; | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server, | ||
641 | struct inode *dir, | ||
642 | const char *path, | ||
643 | __le32 info_mask, | ||
644 | const struct nw_modify_dos_info *info) | ||
645 | { | ||
646 | __u8 volnum = NCP_FINFO(dir)->volNumber; | ||
647 | __le32 dirent = NCP_FINFO(dir)->dirEntNum; | ||
648 | int result; | ||
649 | |||
650 | ncp_init_request(server); | ||
651 | ncp_add_byte(server, 7); /* subfunction */ | ||
652 | ncp_add_byte(server, server->name_space[volnum]); | ||
653 | ncp_add_byte(server, 0); /* reserved */ | ||
654 | ncp_add_word(server, cpu_to_le16(0x8006)); /* search attribs: all */ | ||
655 | |||
656 | ncp_add_dword(server, info_mask); | ||
657 | ncp_add_mem(server, info, sizeof(*info)); | ||
658 | ncp_add_handle_path(server, volnum, dirent, 1, path); | ||
659 | |||
660 | result = ncp_request(server, 87); | ||
661 | ncp_unlock_server(server); | ||
662 | return result; | ||
663 | } | ||
664 | |||
665 | int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, | ||
666 | struct inode *dir, | ||
667 | __le32 info_mask, | ||
668 | const struct nw_modify_dos_info *info) | ||
669 | { | ||
670 | return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL, | ||
671 | info_mask, info); | ||
672 | } | ||
673 | |||
674 | #ifdef CONFIG_NCPFS_NFS_NS | ||
675 | int ncp_modify_nfs_info(struct ncp_server *server, __u8 volnum, __le32 dirent, | ||
676 | __u32 mode, __u32 rdev) | ||
677 | |||
678 | { | ||
679 | int result = 0; | ||
680 | |||
681 | if (server->name_space[volnum] == NW_NS_NFS) { | ||
682 | ncp_init_request(server); | ||
683 | ncp_add_byte(server, 25); /* subfunction */ | ||
684 | ncp_add_byte(server, server->name_space[volnum]); | ||
685 | ncp_add_byte(server, NW_NS_NFS); | ||
686 | ncp_add_byte(server, volnum); | ||
687 | ncp_add_dword(server, dirent); | ||
688 | /* we must always operate on both nlinks and rdev, otherwise | ||
689 | rdev is not set */ | ||
690 | ncp_add_dword_lh(server, NSIBM_NFS_MODE | NSIBM_NFS_NLINKS | NSIBM_NFS_RDEV); | ||
691 | ncp_add_dword_lh(server, mode); | ||
692 | ncp_add_dword_lh(server, 1); /* nlinks */ | ||
693 | ncp_add_dword_lh(server, rdev); | ||
694 | result = ncp_request(server, 87); | ||
695 | ncp_unlock_server(server); | ||
696 | } | ||
697 | return result; | ||
698 | } | ||
699 | #endif | ||
700 | |||
701 | |||
702 | static int | ||
703 | ncp_DeleteNSEntry(struct ncp_server *server, | ||
704 | __u8 have_dir_base, __u8 volnum, __le32 dirent, | ||
705 | char* name, __u8 ns, __le16 attr) | ||
706 | { | ||
707 | int result; | ||
708 | |||
709 | ncp_init_request(server); | ||
710 | ncp_add_byte(server, 8); /* subfunction */ | ||
711 | ncp_add_byte(server, ns); | ||
712 | ncp_add_byte(server, 0); /* reserved */ | ||
713 | ncp_add_word(server, attr); /* search attribs: all */ | ||
714 | ncp_add_handle_path(server, volnum, dirent, have_dir_base, name); | ||
715 | |||
716 | result = ncp_request(server, 87); | ||
717 | ncp_unlock_server(server); | ||
718 | return result; | ||
719 | } | ||
720 | |||
721 | int | ||
722 | ncp_del_file_or_subdir2(struct ncp_server *server, | ||
723 | struct dentry *dentry) | ||
724 | { | ||
725 | struct inode *inode = dentry->d_inode; | ||
726 | __u8 volnum; | ||
727 | __le32 dirent; | ||
728 | |||
729 | if (!inode) { | ||
730 | #ifdef CONFIG_NCPFS_DEBUGDENTRY | ||
731 | PRINTK("ncpfs: ncpdel2: dentry->d_inode == NULL\n"); | ||
732 | #endif | ||
733 | return 0xFF; /* Any error */ | ||
734 | } | ||
735 | volnum = NCP_FINFO(inode)->volNumber; | ||
736 | dirent = NCP_FINFO(inode)->DosDirNum; | ||
737 | return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, cpu_to_le16(0x8006)); | ||
738 | } | ||
739 | |||
740 | int | ||
741 | ncp_del_file_or_subdir(struct ncp_server *server, | ||
742 | struct inode *dir, char *name) | ||
743 | { | ||
744 | __u8 volnum = NCP_FINFO(dir)->volNumber; | ||
745 | __le32 dirent = NCP_FINFO(dir)->dirEntNum; | ||
746 | |||
747 | #ifdef CONFIG_NCPFS_NFS_NS | ||
748 | if (server->name_space[volnum]==NW_NS_NFS) | ||
749 | { | ||
750 | int result; | ||
751 | |||
752 | result=ncp_obtain_DOS_dir_base(server, volnum, dirent, name, &dirent); | ||
753 | if (result) return result; | ||
754 | return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, cpu_to_le16(0x8006)); | ||
755 | } | ||
756 | else | ||
757 | #endif /* CONFIG_NCPFS_NFS_NS */ | ||
758 | return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, server->name_space[volnum], cpu_to_le16(0x8006)); | ||
759 | } | ||
760 | |||
761 | static inline void ConvertToNWfromDWORD(__u16 v0, __u16 v1, __u8 ret[6]) | ||
762 | { | ||
763 | __le16 *dest = (__le16 *) ret; | ||
764 | dest[1] = cpu_to_le16(v0); | ||
765 | dest[2] = cpu_to_le16(v1); | ||
766 | dest[0] = cpu_to_le16(v0 + 1); | ||
767 | return; | ||
768 | } | ||
769 | |||
770 | /* If both dir and name are NULL, then in target there's already a | ||
771 | looked-up entry that wants to be opened. */ | ||
772 | int ncp_open_create_file_or_subdir(struct ncp_server *server, | ||
773 | struct inode *dir, char *name, | ||
774 | int open_create_mode, | ||
775 | __le32 create_attributes, | ||
776 | __le16 desired_acc_rights, | ||
777 | struct ncp_entry_info *target) | ||
778 | { | ||
779 | __le16 search_attribs = cpu_to_le16(0x0006); | ||
780 | __u8 volnum; | ||
781 | __le32 dirent; | ||
782 | int result; | ||
783 | |||
784 | volnum = NCP_FINFO(dir)->volNumber; | ||
785 | dirent = NCP_FINFO(dir)->dirEntNum; | ||
786 | |||
787 | if ((create_attributes & aDIR) != 0) { | ||
788 | search_attribs |= cpu_to_le16(0x8000); | ||
789 | } | ||
790 | ncp_init_request(server); | ||
791 | ncp_add_byte(server, 1); /* subfunction */ | ||
792 | ncp_add_byte(server, server->name_space[volnum]); | ||
793 | ncp_add_byte(server, open_create_mode); | ||
794 | ncp_add_word(server, search_attribs); | ||
795 | ncp_add_dword(server, RIM_ALL); | ||
796 | ncp_add_dword(server, create_attributes); | ||
797 | /* The desired acc rights seem to be the inherited rights mask | ||
798 | for directories */ | ||
799 | ncp_add_word(server, desired_acc_rights); | ||
800 | ncp_add_handle_path(server, volnum, dirent, 1, name); | ||
801 | |||
802 | if ((result = ncp_request(server, 87)) != 0) | ||
803 | goto out; | ||
804 | if (!(create_attributes & aDIR)) | ||
805 | target->opened = 1; | ||
806 | |||
807 | /* in target there's a new finfo to fill */ | ||
808 | ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i)); | ||
809 | target->volume = target->i.volNumber; | ||
810 | ConvertToNWfromDWORD(ncp_reply_le16(server, 0), | ||
811 | ncp_reply_le16(server, 2), | ||
812 | target->file_handle); | ||
813 | |||
814 | ncp_unlock_server(server); | ||
815 | |||
816 | (void)ncp_obtain_nfs_info(server, &(target->i)); | ||
817 | return 0; | ||
818 | |||
819 | out: | ||
820 | ncp_unlock_server(server); | ||
821 | return result; | ||
822 | } | ||
823 | |||
824 | int | ||
825 | ncp_initialize_search(struct ncp_server *server, struct inode *dir, | ||
826 | struct nw_search_sequence *target) | ||
827 | { | ||
828 | __u8 volnum = NCP_FINFO(dir)->volNumber; | ||
829 | __le32 dirent = NCP_FINFO(dir)->dirEntNum; | ||
830 | int result; | ||
831 | |||
832 | ncp_init_request(server); | ||
833 | ncp_add_byte(server, 2); /* subfunction */ | ||
834 | ncp_add_byte(server, server->name_space[volnum]); | ||
835 | ncp_add_byte(server, 0); /* reserved */ | ||
836 | ncp_add_handle_path(server, volnum, dirent, 1, NULL); | ||
837 | |||
838 | result = ncp_request(server, 87); | ||
839 | if (result) | ||
840 | goto out; | ||
841 | memcpy(target, ncp_reply_data(server, 0), sizeof(*target)); | ||
842 | |||
843 | out: | ||
844 | ncp_unlock_server(server); | ||
845 | return result; | ||
846 | } | ||
847 | |||
848 | /* Search for everything */ | ||
849 | int ncp_search_for_file_or_subdir(struct ncp_server *server, | ||
850 | struct nw_search_sequence *seq, | ||
851 | struct nw_info_struct *target) | ||
852 | { | ||
853 | int result; | ||
854 | |||
855 | ncp_init_request(server); | ||
856 | ncp_add_byte(server, 3); /* subfunction */ | ||
857 | ncp_add_byte(server, server->name_space[seq->volNumber]); | ||
858 | ncp_add_byte(server, 0); /* data stream (???) */ | ||
859 | ncp_add_word(server, cpu_to_le16(0x8006)); /* Search attribs */ | ||
860 | ncp_add_dword(server, RIM_ALL); /* return info mask */ | ||
861 | ncp_add_mem(server, seq, 9); | ||
862 | #ifdef CONFIG_NCPFS_NFS_NS | ||
863 | if (server->name_space[seq->volNumber] == NW_NS_NFS) { | ||
864 | ncp_add_byte(server, 0); /* 0 byte pattern */ | ||
865 | } else | ||
866 | #endif | ||
867 | { | ||
868 | ncp_add_byte(server, 2); /* 2 byte pattern */ | ||
869 | ncp_add_byte(server, 0xff); /* following is a wildcard */ | ||
870 | ncp_add_byte(server, '*'); | ||
871 | } | ||
872 | |||
873 | if ((result = ncp_request(server, 87)) != 0) | ||
874 | goto out; | ||
875 | memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq)); | ||
876 | ncp_extract_file_info(ncp_reply_data(server, 10), target); | ||
877 | |||
878 | ncp_unlock_server(server); | ||
879 | |||
880 | result = ncp_obtain_nfs_info(server, target); | ||
881 | return result; | ||
882 | |||
883 | out: | ||
884 | ncp_unlock_server(server); | ||
885 | return result; | ||
886 | } | ||
887 | |||
888 | int ncp_search_for_fileset(struct ncp_server *server, | ||
889 | struct nw_search_sequence *seq, | ||
890 | int* more, | ||
891 | int* cnt, | ||
892 | char* buffer, | ||
893 | size_t bufsize, | ||
894 | char** rbuf, | ||
895 | size_t* rsize) | ||
896 | { | ||
897 | int result; | ||
898 | |||
899 | ncp_init_request(server); | ||
900 | ncp_add_byte(server, 20); | ||
901 | ncp_add_byte(server, server->name_space[seq->volNumber]); | ||
902 | ncp_add_byte(server, 0); /* datastream */ | ||
903 | ncp_add_word(server, cpu_to_le16(0x8006)); | ||
904 | ncp_add_dword(server, RIM_ALL); | ||
905 | ncp_add_word(server, cpu_to_le16(32767)); /* max returned items */ | ||
906 | ncp_add_mem(server, seq, 9); | ||
907 | #ifdef CONFIG_NCPFS_NFS_NS | ||
908 | if (server->name_space[seq->volNumber] == NW_NS_NFS) { | ||
909 | ncp_add_byte(server, 0); /* 0 byte pattern */ | ||
910 | } else | ||
911 | #endif | ||
912 | { | ||
913 | ncp_add_byte(server, 2); /* 2 byte pattern */ | ||
914 | ncp_add_byte(server, 0xff); /* following is a wildcard */ | ||
915 | ncp_add_byte(server, '*'); | ||
916 | } | ||
917 | result = ncp_request2(server, 87, buffer, bufsize); | ||
918 | if (result) { | ||
919 | ncp_unlock_server(server); | ||
920 | return result; | ||
921 | } | ||
922 | if (server->ncp_reply_size < 12) { | ||
923 | ncp_unlock_server(server); | ||
924 | return 0xFF; | ||
925 | } | ||
926 | *rsize = server->ncp_reply_size - 12; | ||
927 | ncp_unlock_server(server); | ||
928 | buffer = buffer + sizeof(struct ncp_reply_header); | ||
929 | *rbuf = buffer + 12; | ||
930 | *cnt = WVAL_LH(buffer + 10); | ||
931 | *more = BVAL(buffer + 9); | ||
932 | memcpy(seq, buffer, 9); | ||
933 | return 0; | ||
934 | } | ||
935 | |||
936 | static int | ||
937 | ncp_RenameNSEntry(struct ncp_server *server, | ||
938 | struct inode *old_dir, char *old_name, __le16 old_type, | ||
939 | struct inode *new_dir, char *new_name) | ||
940 | { | ||
941 | int result = -EINVAL; | ||
942 | |||
943 | if ((old_dir == NULL) || (old_name == NULL) || | ||
944 | (new_dir == NULL) || (new_name == NULL)) | ||
945 | goto out; | ||
946 | |||
947 | ncp_init_request(server); | ||
948 | ncp_add_byte(server, 4); /* subfunction */ | ||
949 | ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]); | ||
950 | ncp_add_byte(server, 1); /* rename flag */ | ||
951 | ncp_add_word(server, old_type); /* search attributes */ | ||
952 | |||
953 | /* source Handle Path */ | ||
954 | ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber); | ||
955 | ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum); | ||
956 | ncp_add_byte(server, 1); | ||
957 | ncp_add_byte(server, 1); /* 1 source component */ | ||
958 | |||
959 | /* dest Handle Path */ | ||
960 | ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber); | ||
961 | ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum); | ||
962 | ncp_add_byte(server, 1); | ||
963 | ncp_add_byte(server, 1); /* 1 destination component */ | ||
964 | |||
965 | /* source path string */ | ||
966 | ncp_add_pstring(server, old_name); | ||
967 | /* dest path string */ | ||
968 | ncp_add_pstring(server, new_name); | ||
969 | |||
970 | result = ncp_request(server, 87); | ||
971 | ncp_unlock_server(server); | ||
972 | out: | ||
973 | return result; | ||
974 | } | ||
975 | |||
976 | int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, | ||
977 | struct inode *old_dir, char *old_name, | ||
978 | struct inode *new_dir, char *new_name) | ||
979 | { | ||
980 | int result; | ||
981 | __le16 old_type = cpu_to_le16(0x06); | ||
982 | |||
983 | /* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */ | ||
984 | result = ncp_RenameNSEntry(server, old_dir, old_name, old_type, | ||
985 | new_dir, new_name); | ||
986 | if (result == 0xFF) /* File Not Found, try directory */ | ||
987 | { | ||
988 | old_type = cpu_to_le16(0x16); | ||
989 | result = ncp_RenameNSEntry(server, old_dir, old_name, old_type, | ||
990 | new_dir, new_name); | ||
991 | } | ||
992 | if (result != 0x92) return result; /* All except NO_FILES_RENAMED */ | ||
993 | result = ncp_del_file_or_subdir(server, new_dir, new_name); | ||
994 | if (result != 0) return -EACCES; | ||
995 | result = ncp_RenameNSEntry(server, old_dir, old_name, old_type, | ||
996 | new_dir, new_name); | ||
997 | return result; | ||
998 | } | ||
999 | |||
1000 | |||
1001 | /* We have to transfer to/from user space */ | ||
1002 | int | ||
1003 | ncp_read_kernel(struct ncp_server *server, const char *file_id, | ||
1004 | __u32 offset, __u16 to_read, char *target, int *bytes_read) | ||
1005 | { | ||
1006 | char *source; | ||
1007 | int result; | ||
1008 | |||
1009 | ncp_init_request(server); | ||
1010 | ncp_add_byte(server, 0); | ||
1011 | ncp_add_mem(server, file_id, 6); | ||
1012 | ncp_add_be32(server, offset); | ||
1013 | ncp_add_be16(server, to_read); | ||
1014 | |||
1015 | if ((result = ncp_request(server, 72)) != 0) { | ||
1016 | goto out; | ||
1017 | } | ||
1018 | *bytes_read = ncp_reply_be16(server, 0); | ||
1019 | source = ncp_reply_data(server, 2 + (offset & 1)); | ||
1020 | |||
1021 | memcpy(target, source, *bytes_read); | ||
1022 | out: | ||
1023 | ncp_unlock_server(server); | ||
1024 | return result; | ||
1025 | } | ||
1026 | |||
1027 | /* There is a problem... egrep and some other silly tools do: | ||
1028 | x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768); | ||
1029 | read(<ncpfs fd>, x, 32768); | ||
1030 | Now copying read result by copy_to_user causes pagefault. This pagefault | ||
1031 | could not be handled because of server was locked due to read. So we have | ||
1032 | to use temporary buffer. So ncp_unlock_server must be done before | ||
1033 | copy_to_user (and for write, copy_from_user must be done before | ||
1034 | ncp_init_request... same applies for send raw packet ioctl). Because of | ||
1035 | file is normally read in bigger chunks, caller provides kmalloced | ||
1036 | (vmalloced) chunk of memory with size >= to_read... | ||
1037 | */ | ||
1038 | int | ||
1039 | ncp_read_bounce(struct ncp_server *server, const char *file_id, | ||
1040 | __u32 offset, __u16 to_read, char __user *target, int *bytes_read, | ||
1041 | void* bounce, __u32 bufsize) | ||
1042 | { | ||
1043 | int result; | ||
1044 | |||
1045 | ncp_init_request(server); | ||
1046 | ncp_add_byte(server, 0); | ||
1047 | ncp_add_mem(server, file_id, 6); | ||
1048 | ncp_add_be32(server, offset); | ||
1049 | ncp_add_be16(server, to_read); | ||
1050 | result = ncp_request2(server, 72, bounce, bufsize); | ||
1051 | ncp_unlock_server(server); | ||
1052 | if (!result) { | ||
1053 | int len = be16_to_cpu(get_unaligned((__be16*)((char*)bounce + | ||
1054 | sizeof(struct ncp_reply_header)))); | ||
1055 | result = -EIO; | ||
1056 | if (len <= to_read) { | ||
1057 | char* source; | ||
1058 | |||
1059 | source = (char*)bounce + | ||
1060 | sizeof(struct ncp_reply_header) + 2 + | ||
1061 | (offset & 1); | ||
1062 | *bytes_read = len; | ||
1063 | result = 0; | ||
1064 | if (copy_to_user(target, source, len)) | ||
1065 | result = -EFAULT; | ||
1066 | } | ||
1067 | } | ||
1068 | return result; | ||
1069 | } | ||
1070 | |||
1071 | int | ||
1072 | ncp_write_kernel(struct ncp_server *server, const char *file_id, | ||
1073 | __u32 offset, __u16 to_write, | ||
1074 | const char *source, int *bytes_written) | ||
1075 | { | ||
1076 | int result; | ||
1077 | |||
1078 | ncp_init_request(server); | ||
1079 | ncp_add_byte(server, 0); | ||
1080 | ncp_add_mem(server, file_id, 6); | ||
1081 | ncp_add_be32(server, offset); | ||
1082 | ncp_add_be16(server, to_write); | ||
1083 | ncp_add_mem(server, source, to_write); | ||
1084 | |||
1085 | if ((result = ncp_request(server, 73)) == 0) | ||
1086 | *bytes_written = to_write; | ||
1087 | ncp_unlock_server(server); | ||
1088 | return result; | ||
1089 | } | ||
1090 | |||
1091 | #ifdef CONFIG_NCPFS_IOCTL_LOCKING | ||
1092 | int | ||
1093 | ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id, | ||
1094 | __u8 locktype, __u32 offset, __u32 length, __u16 timeout) | ||
1095 | { | ||
1096 | int result; | ||
1097 | |||
1098 | ncp_init_request(server); | ||
1099 | ncp_add_byte(server, locktype); | ||
1100 | ncp_add_mem(server, file_id, 6); | ||
1101 | ncp_add_be32(server, offset); | ||
1102 | ncp_add_be32(server, length); | ||
1103 | ncp_add_be16(server, timeout); | ||
1104 | |||
1105 | if ((result = ncp_request(server, 0x1A)) != 0) | ||
1106 | { | ||
1107 | ncp_unlock_server(server); | ||
1108 | return result; | ||
1109 | } | ||
1110 | ncp_unlock_server(server); | ||
1111 | return 0; | ||
1112 | } | ||
1113 | |||
1114 | int | ||
1115 | ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id, | ||
1116 | __u32 offset, __u32 length) | ||
1117 | { | ||
1118 | int result; | ||
1119 | |||
1120 | ncp_init_request(server); | ||
1121 | ncp_add_byte(server, 0); /* who knows... lanalyzer says that */ | ||
1122 | ncp_add_mem(server, file_id, 6); | ||
1123 | ncp_add_be32(server, offset); | ||
1124 | ncp_add_be32(server, length); | ||
1125 | |||
1126 | if ((result = ncp_request(server, 0x1E)) != 0) | ||
1127 | { | ||
1128 | ncp_unlock_server(server); | ||
1129 | return result; | ||
1130 | } | ||
1131 | ncp_unlock_server(server); | ||
1132 | return 0; | ||
1133 | } | ||
1134 | #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ | ||
1135 | |||
1136 | #ifdef CONFIG_NCPFS_NLS | ||
1137 | /* This are the NLS conversion routines with inspirations and code parts | ||
1138 | * from the vfat file system and hints from Petr Vandrovec. | ||
1139 | */ | ||
1140 | |||
1141 | int | ||
1142 | ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen, | ||
1143 | const unsigned char *iname, unsigned int ilen, int cc) | ||
1144 | { | ||
1145 | struct nls_table *in = server->nls_io; | ||
1146 | struct nls_table *out = server->nls_vol; | ||
1147 | unsigned char *vname_start; | ||
1148 | unsigned char *vname_end; | ||
1149 | const unsigned char *iname_end; | ||
1150 | |||
1151 | iname_end = iname + ilen; | ||
1152 | vname_start = vname; | ||
1153 | vname_end = vname + *vlen - 1; | ||
1154 | |||
1155 | while (iname < iname_end) { | ||
1156 | int chl; | ||
1157 | wchar_t ec; | ||
1158 | |||
1159 | if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) { | ||
1160 | int k; | ||
1161 | |||
1162 | k = utf8_mbtowc(&ec, iname, iname_end - iname); | ||
1163 | if (k < 0) | ||
1164 | return -EINVAL; | ||
1165 | iname += k; | ||
1166 | } else { | ||
1167 | if (*iname == NCP_ESC) { | ||
1168 | int k; | ||
1169 | |||
1170 | if (iname_end - iname < 5) | ||
1171 | goto nospec; | ||
1172 | |||
1173 | ec = 0; | ||
1174 | for (k = 1; k < 5; k++) { | ||
1175 | unsigned char nc; | ||
1176 | |||
1177 | nc = iname[k] - '0'; | ||
1178 | if (nc >= 10) { | ||
1179 | nc -= 'A' - '0' - 10; | ||
1180 | if ((nc < 10) || (nc > 15)) { | ||
1181 | goto nospec; | ||
1182 | } | ||
1183 | } | ||
1184 | ec = (ec << 4) | nc; | ||
1185 | } | ||
1186 | iname += 5; | ||
1187 | } else { | ||
1188 | nospec:; | ||
1189 | if ( (chl = in->char2uni(iname, iname_end - iname, &ec)) < 0) | ||
1190 | return chl; | ||
1191 | iname += chl; | ||
1192 | } | ||
1193 | } | ||
1194 | |||
1195 | /* unitoupper should be here! */ | ||
1196 | |||
1197 | chl = out->uni2char(ec, vname, vname_end - vname); | ||
1198 | if (chl < 0) | ||
1199 | return chl; | ||
1200 | |||
1201 | /* this is wrong... */ | ||
1202 | if (cc) { | ||
1203 | int chi; | ||
1204 | |||
1205 | for (chi = 0; chi < chl; chi++){ | ||
1206 | vname[chi] = ncp_toupper(out, vname[chi]); | ||
1207 | } | ||
1208 | } | ||
1209 | vname += chl; | ||
1210 | } | ||
1211 | |||
1212 | *vname = 0; | ||
1213 | *vlen = vname - vname_start; | ||
1214 | return 0; | ||
1215 | } | ||
1216 | |||
1217 | int | ||
1218 | ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen, | ||
1219 | const unsigned char *vname, unsigned int vlen, int cc) | ||
1220 | { | ||
1221 | struct nls_table *in = server->nls_vol; | ||
1222 | struct nls_table *out = server->nls_io; | ||
1223 | const unsigned char *vname_end; | ||
1224 | unsigned char *iname_start; | ||
1225 | unsigned char *iname_end; | ||
1226 | unsigned char *vname_cc; | ||
1227 | int err; | ||
1228 | |||
1229 | vname_cc = NULL; | ||
1230 | |||
1231 | if (cc) { | ||
1232 | int i; | ||
1233 | |||
1234 | /* this is wrong! */ | ||
1235 | vname_cc = kmalloc(vlen, GFP_KERNEL); | ||
1236 | if (!vname_cc) | ||
1237 | return -ENOMEM; | ||
1238 | for (i = 0; i < vlen; i++) | ||
1239 | vname_cc[i] = ncp_tolower(in, vname[i]); | ||
1240 | vname = vname_cc; | ||
1241 | } | ||
1242 | |||
1243 | iname_start = iname; | ||
1244 | iname_end = iname + *ilen - 1; | ||
1245 | vname_end = vname + vlen; | ||
1246 | |||
1247 | while (vname < vname_end) { | ||
1248 | wchar_t ec; | ||
1249 | int chl; | ||
1250 | |||
1251 | if ( (chl = in->char2uni(vname, vname_end - vname, &ec)) < 0) { | ||
1252 | err = chl; | ||
1253 | goto quit; | ||
1254 | } | ||
1255 | vname += chl; | ||
1256 | |||
1257 | /* unitolower should be here! */ | ||
1258 | |||
1259 | if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) { | ||
1260 | int k; | ||
1261 | |||
1262 | k = utf8_wctomb(iname, ec, iname_end - iname); | ||
1263 | if (k < 0) { | ||
1264 | err = -ENAMETOOLONG; | ||
1265 | goto quit; | ||
1266 | } | ||
1267 | iname += k; | ||
1268 | } else { | ||
1269 | if ( (chl = out->uni2char(ec, iname, iname_end - iname)) >= 0) { | ||
1270 | iname += chl; | ||
1271 | } else { | ||
1272 | int k; | ||
1273 | |||
1274 | if (iname_end - iname < 5) { | ||
1275 | err = -ENAMETOOLONG; | ||
1276 | goto quit; | ||
1277 | } | ||
1278 | *iname = NCP_ESC; | ||
1279 | for (k = 4; k > 0; k--) { | ||
1280 | unsigned char v; | ||
1281 | |||
1282 | v = (ec & 0xF) + '0'; | ||
1283 | if (v > '9') { | ||
1284 | v += 'A' - '9' - 1; | ||
1285 | } | ||
1286 | iname[k] = v; | ||
1287 | ec >>= 4; | ||
1288 | } | ||
1289 | iname += 5; | ||
1290 | } | ||
1291 | } | ||
1292 | } | ||
1293 | |||
1294 | *iname = 0; | ||
1295 | *ilen = iname - iname_start; | ||
1296 | err = 0; | ||
1297 | quit:; | ||
1298 | if (cc) | ||
1299 | kfree(vname_cc); | ||
1300 | return err; | ||
1301 | } | ||
1302 | |||
1303 | #else | ||
1304 | |||
1305 | int | ||
1306 | ncp__io2vol(unsigned char *vname, unsigned int *vlen, | ||
1307 | const unsigned char *iname, unsigned int ilen, int cc) | ||
1308 | { | ||
1309 | int i; | ||
1310 | |||
1311 | if (*vlen <= ilen) | ||
1312 | return -ENAMETOOLONG; | ||
1313 | |||
1314 | if (cc) | ||
1315 | for (i = 0; i < ilen; i++) { | ||
1316 | *vname = toupper(*iname); | ||
1317 | vname++; | ||
1318 | iname++; | ||
1319 | } | ||
1320 | else { | ||
1321 | memmove(vname, iname, ilen); | ||
1322 | vname += ilen; | ||
1323 | } | ||
1324 | |||
1325 | *vlen = ilen; | ||
1326 | *vname = 0; | ||
1327 | return 0; | ||
1328 | } | ||
1329 | |||
1330 | int | ||
1331 | ncp__vol2io(unsigned char *iname, unsigned int *ilen, | ||
1332 | const unsigned char *vname, unsigned int vlen, int cc) | ||
1333 | { | ||
1334 | int i; | ||
1335 | |||
1336 | if (*ilen <= vlen) | ||
1337 | return -ENAMETOOLONG; | ||
1338 | |||
1339 | if (cc) | ||
1340 | for (i = 0; i < vlen; i++) { | ||
1341 | *iname = tolower(*vname); | ||
1342 | iname++; | ||
1343 | vname++; | ||
1344 | } | ||
1345 | else { | ||
1346 | memmove(iname, vname, vlen); | ||
1347 | iname += vlen; | ||
1348 | } | ||
1349 | |||
1350 | *ilen = vlen; | ||
1351 | *iname = 0; | ||
1352 | return 0; | ||
1353 | } | ||
1354 | |||
1355 | #endif | ||
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h new file mode 100644 index 000000000000..05ec2e9d90c6 --- /dev/null +++ b/fs/ncpfs/ncplib_kernel.h | |||
@@ -0,0 +1,259 @@ | |||
1 | /* | ||
2 | * ncplib_kernel.h | ||
3 | * | ||
4 | * Copyright (C) 1995, 1996 by Volker Lendecke | ||
5 | * Modified for big endian by J.F. Chadima and David S. Miller | ||
6 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
7 | * Modified 1998, 1999 Wolfram Pienkoss for NLS | ||
8 | * Modified 1999 Wolfram Pienkoss for directory caching | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #ifndef _NCPLIB_H | ||
13 | #define _NCPLIB_H | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | |||
17 | #include <linux/fs.h> | ||
18 | #include <linux/types.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/stat.h> | ||
22 | #include <linux/fcntl.h> | ||
23 | #include <linux/pagemap.h> | ||
24 | |||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/byteorder.h> | ||
27 | #include <asm/unaligned.h> | ||
28 | #include <asm/string.h> | ||
29 | |||
30 | #ifdef CONFIG_NCPFS_NLS | ||
31 | #include <linux/nls.h> | ||
32 | #else | ||
33 | #include <linux/ctype.h> | ||
34 | #endif /* CONFIG_NCPFS_NLS */ | ||
35 | |||
36 | #include <linux/ncp_fs.h> | ||
37 | |||
38 | #define NCP_MIN_SYMLINK_SIZE 8 | ||
39 | #define NCP_MAX_SYMLINK_SIZE 512 | ||
40 | |||
41 | #define NCP_BLOCK_SHIFT 9 | ||
42 | #define NCP_BLOCK_SIZE (1 << (NCP_BLOCK_SHIFT)) | ||
43 | |||
44 | int ncp_negotiate_buffersize(struct ncp_server *, int, int *); | ||
45 | int ncp_negotiate_size_and_options(struct ncp_server *server, int size, | ||
46 | int options, int *ret_size, int *ret_options); | ||
47 | |||
48 | int ncp_get_volume_info_with_number(struct ncp_server* server, int n, | ||
49 | struct ncp_volume_info *target); | ||
50 | |||
51 | int ncp_get_directory_info(struct ncp_server* server, __u8 dirhandle, | ||
52 | struct ncp_volume_info* target); | ||
53 | |||
54 | int ncp_close_file(struct ncp_server *, const char *); | ||
55 | static inline int ncp_read_bounce_size(__u32 size) { | ||
56 | return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8; | ||
57 | }; | ||
58 | int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16, | ||
59 | char __user *, int *, void* bounce, __u32 bouncelen); | ||
60 | int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, | ||
61 | char *, int *); | ||
62 | int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16, | ||
63 | const char *, int *); | ||
64 | |||
65 | static inline void ncp_inode_close(struct inode *inode) { | ||
66 | atomic_dec(&NCP_FINFO(inode)->opened); | ||
67 | } | ||
68 | |||
69 | void ncp_extract_file_info(void* src, struct nw_info_struct* target); | ||
70 | int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, | ||
71 | struct nw_info_struct *target); | ||
72 | int ncp_obtain_nfs_info(struct ncp_server *server, struct nw_info_struct *target); | ||
73 | int ncp_get_volume_root(struct ncp_server *server, const char *volname, | ||
74 | __u32 *volume, __le32 *dirent, __le32 *dosdirent); | ||
75 | int ncp_lookup_volume(struct ncp_server *, const char *, struct nw_info_struct *); | ||
76 | int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *, | ||
77 | __le32, const struct nw_modify_dos_info *info); | ||
78 | int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *, struct inode *, | ||
79 | const char* path, __le32, const struct nw_modify_dos_info *info); | ||
80 | int ncp_modify_nfs_info(struct ncp_server *, __u8 volnum, __le32 dirent, | ||
81 | __u32 mode, __u32 rdev); | ||
82 | |||
83 | int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*); | ||
84 | int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *); | ||
85 | int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *, | ||
86 | int, __le32, __le16, struct ncp_entry_info *); | ||
87 | |||
88 | int ncp_initialize_search(struct ncp_server *, struct inode *, | ||
89 | struct nw_search_sequence *target); | ||
90 | int ncp_search_for_file_or_subdir(struct ncp_server *server, | ||
91 | struct nw_search_sequence *seq, | ||
92 | struct nw_info_struct *target); | ||
93 | int ncp_search_for_fileset(struct ncp_server *server, | ||
94 | struct nw_search_sequence *seq, | ||
95 | int* more, int* cnt, | ||
96 | char* buffer, size_t bufsize, | ||
97 | char** rbuf, size_t* rsize); | ||
98 | |||
99 | int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, | ||
100 | struct inode *, char *, struct inode *, char *); | ||
101 | |||
102 | |||
103 | int | ||
104 | ncp_LogPhysicalRecord(struct ncp_server *server, | ||
105 | const char *file_id, __u8 locktype, | ||
106 | __u32 offset, __u32 length, __u16 timeout); | ||
107 | |||
108 | #ifdef CONFIG_NCPFS_IOCTL_LOCKING | ||
109 | int | ||
110 | ncp_ClearPhysicalRecord(struct ncp_server *server, | ||
111 | const char *file_id, | ||
112 | __u32 offset, __u32 length); | ||
113 | #endif /* CONFIG_NCPFS_IOCTL_LOCKING */ | ||
114 | |||
115 | int | ||
116 | ncp_mount_subdir(struct ncp_server *, __u8, __u8, __le32, | ||
117 | __u32* volume, __le32* dirent, __le32* dosdirent); | ||
118 | int ncp_dirhandle_alloc(struct ncp_server *, __u8 vol, __le32 dirent, __u8 *dirhandle); | ||
119 | int ncp_dirhandle_free(struct ncp_server *, __u8 dirhandle); | ||
120 | |||
121 | int ncp_create_new(struct inode *dir, struct dentry *dentry, | ||
122 | int mode, dev_t rdev, __le32 attributes); | ||
123 | |||
124 | static inline int ncp_is_nfs_extras(struct ncp_server* server, unsigned int volnum) { | ||
125 | #ifdef CONFIG_NCPFS_NFS_NS | ||
126 | return (server->m.flags & NCP_MOUNT_NFS_EXTRAS) && | ||
127 | (server->name_space[volnum] == NW_NS_NFS); | ||
128 | #else | ||
129 | return 0; | ||
130 | #endif | ||
131 | } | ||
132 | |||
133 | #ifdef CONFIG_NCPFS_NLS | ||
134 | |||
135 | int ncp__io2vol(struct ncp_server *, unsigned char *, unsigned int *, | ||
136 | const unsigned char *, unsigned int, int); | ||
137 | int ncp__vol2io(struct ncp_server *, unsigned char *, unsigned int *, | ||
138 | const unsigned char *, unsigned int, int); | ||
139 | |||
140 | #define NCP_ESC ':' | ||
141 | #define NCP_IO_TABLE(dentry) (NCP_SERVER((dentry)->d_inode)->nls_io) | ||
142 | #define ncp_tolower(t, c) nls_tolower(t, c) | ||
143 | #define ncp_toupper(t, c) nls_toupper(t, c) | ||
144 | #define ncp_strnicmp(t, s1, s2, len) \ | ||
145 | nls_strnicmp(t, s1, s2, len) | ||
146 | #define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(S,m,i,n,k,U) | ||
147 | #define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(S,m,i,n,k,U) | ||
148 | |||
149 | #else | ||
150 | |||
151 | int ncp__io2vol(unsigned char *, unsigned int *, | ||
152 | const unsigned char *, unsigned int, int); | ||
153 | int ncp__vol2io(unsigned char *, unsigned int *, | ||
154 | const unsigned char *, unsigned int, int); | ||
155 | |||
156 | #define NCP_IO_TABLE(dentry) NULL | ||
157 | #define ncp_tolower(t, c) tolower(c) | ||
158 | #define ncp_toupper(t, c) toupper(c) | ||
159 | #define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(m,i,n,k,U) | ||
160 | #define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(m,i,n,k,U) | ||
161 | |||
162 | |||
163 | static inline int ncp_strnicmp(struct nls_table *t, const unsigned char *s1, | ||
164 | const unsigned char *s2, int len) | ||
165 | { | ||
166 | while (len--) { | ||
167 | if (tolower(*s1++) != tolower(*s2++)) | ||
168 | return 1; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | #endif /* CONFIG_NCPFS_NLS */ | ||
175 | |||
176 | #define NCP_GET_AGE(dentry) (jiffies - (dentry)->d_time) | ||
177 | #define NCP_MAX_AGE(server) ((server)->dentry_ttl) | ||
178 | #define NCP_TEST_AGE(server,dentry) (NCP_GET_AGE(dentry) < NCP_MAX_AGE(server)) | ||
179 | |||
180 | static inline void | ||
181 | ncp_age_dentry(struct ncp_server* server, struct dentry* dentry) | ||
182 | { | ||
183 | dentry->d_time = jiffies - server->dentry_ttl; | ||
184 | } | ||
185 | |||
186 | static inline void | ||
187 | ncp_new_dentry(struct dentry* dentry) | ||
188 | { | ||
189 | dentry->d_time = jiffies; | ||
190 | } | ||
191 | |||
192 | static inline void | ||
193 | ncp_renew_dentries(struct dentry *parent) | ||
194 | { | ||
195 | struct ncp_server *server = NCP_SERVER(parent->d_inode); | ||
196 | struct list_head *next; | ||
197 | struct dentry *dentry; | ||
198 | |||
199 | spin_lock(&dcache_lock); | ||
200 | next = parent->d_subdirs.next; | ||
201 | while (next != &parent->d_subdirs) { | ||
202 | dentry = list_entry(next, struct dentry, d_child); | ||
203 | |||
204 | if (dentry->d_fsdata == NULL) | ||
205 | ncp_age_dentry(server, dentry); | ||
206 | else | ||
207 | ncp_new_dentry(dentry); | ||
208 | |||
209 | next = next->next; | ||
210 | } | ||
211 | spin_unlock(&dcache_lock); | ||
212 | } | ||
213 | |||
214 | static inline void | ||
215 | ncp_invalidate_dircache_entries(struct dentry *parent) | ||
216 | { | ||
217 | struct ncp_server *server = NCP_SERVER(parent->d_inode); | ||
218 | struct list_head *next; | ||
219 | struct dentry *dentry; | ||
220 | |||
221 | spin_lock(&dcache_lock); | ||
222 | next = parent->d_subdirs.next; | ||
223 | while (next != &parent->d_subdirs) { | ||
224 | dentry = list_entry(next, struct dentry, d_child); | ||
225 | dentry->d_fsdata = NULL; | ||
226 | ncp_age_dentry(server, dentry); | ||
227 | next = next->next; | ||
228 | } | ||
229 | spin_unlock(&dcache_lock); | ||
230 | } | ||
231 | |||
232 | struct ncp_cache_head { | ||
233 | time_t mtime; | ||
234 | unsigned long time; /* cache age */ | ||
235 | unsigned long end; /* last valid fpos in cache */ | ||
236 | int eof; | ||
237 | }; | ||
238 | |||
239 | #define NCP_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *))) | ||
240 | union ncp_dir_cache { | ||
241 | struct ncp_cache_head head; | ||
242 | struct dentry *dentry[NCP_DIRCACHE_SIZE]; | ||
243 | }; | ||
244 | |||
245 | #define NCP_FIRSTCACHE_SIZE ((int)((NCP_DIRCACHE_SIZE * \ | ||
246 | sizeof(struct dentry *) - sizeof(struct ncp_cache_head)) / \ | ||
247 | sizeof(struct dentry *))) | ||
248 | |||
249 | #define NCP_DIRCACHE_START (NCP_DIRCACHE_SIZE - NCP_FIRSTCACHE_SIZE) | ||
250 | |||
251 | struct ncp_cache_control { | ||
252 | struct ncp_cache_head head; | ||
253 | struct page *page; | ||
254 | union ncp_dir_cache *cache; | ||
255 | unsigned long fpos, ofs; | ||
256 | int filled, valid, idx; | ||
257 | }; | ||
258 | |||
259 | #endif /* _NCPLIB_H */ | ||
diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c new file mode 100644 index 000000000000..a6ec90cd8894 --- /dev/null +++ b/fs/ncpfs/ncpsign_kernel.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * ncpsign_kernel.c | ||
3 | * | ||
4 | * Arne de Bruijn (arne@knoware.nl), 1997 | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #include <linux/config.h> | ||
9 | |||
10 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
11 | |||
12 | #include <linux/string.h> | ||
13 | #include <linux/ncp.h> | ||
14 | #include <linux/bitops.h> | ||
15 | #include "ncpsign_kernel.h" | ||
16 | |||
17 | /* i386: 32-bit, little endian, handles mis-alignment */ | ||
18 | #ifdef __i386__ | ||
19 | #define GET_LE32(p) (*(int *)(p)) | ||
20 | #define PUT_LE32(p,v) { *(int *)(p)=v; } | ||
21 | #else | ||
22 | /* from include/ncplib.h */ | ||
23 | #define BVAL(buf,pos) (((__u8 *)(buf))[pos]) | ||
24 | #define PVAL(buf,pos) ((unsigned)BVAL(buf,pos)) | ||
25 | #define BSET(buf,pos,val) (BVAL(buf,pos) = (val)) | ||
26 | |||
27 | static inline __u16 | ||
28 | WVAL_LH(__u8 * buf, int pos) | ||
29 | { | ||
30 | return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8; | ||
31 | } | ||
32 | static inline __u32 | ||
33 | DVAL_LH(__u8 * buf, int pos) | ||
34 | { | ||
35 | return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16; | ||
36 | } | ||
37 | static inline void | ||
38 | WSET_LH(__u8 * buf, int pos, __u16 val) | ||
39 | { | ||
40 | BSET(buf, pos, val & 0xff); | ||
41 | BSET(buf, pos + 1, val >> 8); | ||
42 | } | ||
43 | static inline void | ||
44 | DSET_LH(__u8 * buf, int pos, __u32 val) | ||
45 | { | ||
46 | WSET_LH(buf, pos, val & 0xffff); | ||
47 | WSET_LH(buf, pos + 2, val >> 16); | ||
48 | } | ||
49 | |||
50 | #define GET_LE32(p) DVAL_LH(p,0) | ||
51 | #define PUT_LE32(p,v) DSET_LH(p,0,v) | ||
52 | #endif | ||
53 | |||
54 | static void nwsign(char *r_data1, char *r_data2, char *outdata) { | ||
55 | int i; | ||
56 | unsigned int w0,w1,w2,w3; | ||
57 | static int rbit[4]={0, 2, 1, 3}; | ||
58 | #ifdef __i386__ | ||
59 | unsigned int *data2=(int *)r_data2; | ||
60 | #else | ||
61 | unsigned int data2[16]; | ||
62 | for (i=0;i<16;i++) | ||
63 | data2[i]=GET_LE32(r_data2+(i<<2)); | ||
64 | #endif | ||
65 | w0=GET_LE32(r_data1); | ||
66 | w1=GET_LE32(r_data1+4); | ||
67 | w2=GET_LE32(r_data1+8); | ||
68 | w3=GET_LE32(r_data1+12); | ||
69 | for (i=0;i<16;i+=4) { | ||
70 | w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3); | ||
71 | w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7); | ||
72 | w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11); | ||
73 | w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19); | ||
74 | } | ||
75 | for (i=0;i<4;i++) { | ||
76 | w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3); | ||
77 | w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5); | ||
78 | w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9); | ||
79 | w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13); | ||
80 | } | ||
81 | for (i=0;i<4;i++) { | ||
82 | w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3); | ||
83 | w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9); | ||
84 | w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11); | ||
85 | w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15); | ||
86 | } | ||
87 | PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff); | ||
88 | PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff); | ||
89 | PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff); | ||
90 | PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff); | ||
91 | } | ||
92 | |||
93 | /* Make a signature for the current packet and add it at the end of the */ | ||
94 | /* packet. */ | ||
95 | void __sign_packet(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, void *sign_buff) { | ||
96 | unsigned char data[64]; | ||
97 | |||
98 | memcpy(data, server->sign_root, 8); | ||
99 | *(__u32*)(data + 8) = totalsize; | ||
100 | if (size < 52) { | ||
101 | memcpy(data + 12, packet, size); | ||
102 | memset(data + 12 + size, 0, 52 - size); | ||
103 | } else { | ||
104 | memcpy(data + 12, packet, 52); | ||
105 | } | ||
106 | nwsign(server->sign_last, data, server->sign_last); | ||
107 | memcpy(sign_buff, server->sign_last, 8); | ||
108 | } | ||
109 | |||
110 | int sign_verify_reply(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, const void *sign_buff) { | ||
111 | unsigned char data[64]; | ||
112 | unsigned char hash[16]; | ||
113 | |||
114 | memcpy(data, server->sign_root, 8); | ||
115 | *(__u32*)(data + 8) = totalsize; | ||
116 | if (size < 52) { | ||
117 | memcpy(data + 12, packet, size); | ||
118 | memset(data + 12 + size, 0, 52 - size); | ||
119 | } else { | ||
120 | memcpy(data + 12, packet, 52); | ||
121 | } | ||
122 | nwsign(server->sign_last, data, hash); | ||
123 | return memcmp(sign_buff, hash, 8); | ||
124 | } | ||
125 | |||
126 | #endif /* CONFIG_NCPFS_PACKET_SIGNING */ | ||
127 | |||
diff --git a/fs/ncpfs/ncpsign_kernel.h b/fs/ncpfs/ncpsign_kernel.h new file mode 100644 index 000000000000..6451a68381cc --- /dev/null +++ b/fs/ncpfs/ncpsign_kernel.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * ncpsign_kernel.h | ||
3 | * | ||
4 | * Arne de Bruijn (arne@knoware.nl), 1997 | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #ifndef _NCPSIGN_KERNEL_H | ||
9 | #define _NCPSIGN_KERNEL_H | ||
10 | |||
11 | #include <linux/ncp_fs.h> | ||
12 | |||
13 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
14 | void __sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff); | ||
15 | int sign_verify_reply(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, const void *sign_buff); | ||
16 | #endif | ||
17 | |||
18 | static inline size_t sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff) { | ||
19 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
20 | if (server->sign_active) { | ||
21 | __sign_packet(server, data, size, totalsize, sign_buff); | ||
22 | return 8; | ||
23 | } | ||
24 | #endif | ||
25 | return 0; | ||
26 | } | ||
27 | |||
28 | #endif | ||
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c new file mode 100644 index 000000000000..6593a5ca88ba --- /dev/null +++ b/fs/ncpfs/sock.c | |||
@@ -0,0 +1,850 @@ | |||
1 | /* | ||
2 | * linux/fs/ncpfs/sock.c | ||
3 | * | ||
4 | * Copyright (C) 1992, 1993 Rick Sladkey | ||
5 | * | ||
6 | * Modified 1995, 1996 by Volker Lendecke to be usable for ncp | ||
7 | * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | |||
13 | #include <linux/time.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/socket.h> | ||
16 | #include <linux/fcntl.h> | ||
17 | #include <linux/stat.h> | ||
18 | #include <asm/uaccess.h> | ||
19 | #include <linux/in.h> | ||
20 | #include <linux/net.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/netdevice.h> | ||
23 | #include <linux/signal.h> | ||
24 | #include <net/scm.h> | ||
25 | #include <net/sock.h> | ||
26 | #include <linux/ipx.h> | ||
27 | #include <linux/poll.h> | ||
28 | #include <linux/file.h> | ||
29 | |||
30 | #include <linux/ncp_fs.h> | ||
31 | |||
32 | #include "ncpsign_kernel.h" | ||
33 | |||
34 | static int _recv(struct socket *sock, void *buf, int size, unsigned flags) | ||
35 | { | ||
36 | struct msghdr msg = {NULL, }; | ||
37 | struct kvec iov = {buf, size}; | ||
38 | return kernel_recvmsg(sock, &msg, &iov, 1, size, flags); | ||
39 | } | ||
40 | |||
41 | static inline int do_send(struct socket *sock, struct kvec *vec, int count, | ||
42 | int len, unsigned flags) | ||
43 | { | ||
44 | struct msghdr msg = { .msg_flags = flags }; | ||
45 | return kernel_sendmsg(sock, &msg, vec, count, len); | ||
46 | } | ||
47 | |||
48 | static int _send(struct socket *sock, const void *buff, int len) | ||
49 | { | ||
50 | struct kvec vec; | ||
51 | vec.iov_base = (void *) buff; | ||
52 | vec.iov_len = len; | ||
53 | return do_send(sock, &vec, 1, len, 0); | ||
54 | } | ||
55 | |||
56 | struct ncp_request_reply { | ||
57 | struct list_head req; | ||
58 | wait_queue_head_t wq; | ||
59 | struct ncp_reply_header* reply_buf; | ||
60 | size_t datalen; | ||
61 | int result; | ||
62 | enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status; | ||
63 | struct kvec* tx_ciov; | ||
64 | size_t tx_totallen; | ||
65 | size_t tx_iovlen; | ||
66 | struct kvec tx_iov[3]; | ||
67 | u_int16_t tx_type; | ||
68 | u_int32_t sign[6]; | ||
69 | }; | ||
70 | |||
71 | void ncp_tcp_data_ready(struct sock *sk, int len) | ||
72 | { | ||
73 | struct ncp_server *server = sk->sk_user_data; | ||
74 | |||
75 | server->data_ready(sk, len); | ||
76 | schedule_work(&server->rcv.tq); | ||
77 | } | ||
78 | |||
79 | void ncp_tcp_error_report(struct sock *sk) | ||
80 | { | ||
81 | struct ncp_server *server = sk->sk_user_data; | ||
82 | |||
83 | server->error_report(sk); | ||
84 | schedule_work(&server->rcv.tq); | ||
85 | } | ||
86 | |||
87 | void ncp_tcp_write_space(struct sock *sk) | ||
88 | { | ||
89 | struct ncp_server *server = sk->sk_user_data; | ||
90 | |||
91 | /* We do not need any locking: we first set tx.creq, and then we do sendmsg, | ||
92 | not vice versa... */ | ||
93 | server->write_space(sk); | ||
94 | if (server->tx.creq) | ||
95 | schedule_work(&server->tx.tq); | ||
96 | } | ||
97 | |||
98 | void ncpdgram_timeout_call(unsigned long v) | ||
99 | { | ||
100 | struct ncp_server *server = (void*)v; | ||
101 | |||
102 | schedule_work(&server->timeout_tq); | ||
103 | } | ||
104 | |||
105 | static inline void ncp_finish_request(struct ncp_request_reply *req, int result) | ||
106 | { | ||
107 | req->result = result; | ||
108 | req->status = RQ_DONE; | ||
109 | wake_up_all(&req->wq); | ||
110 | } | ||
111 | |||
112 | static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) | ||
113 | { | ||
114 | struct ncp_request_reply *req; | ||
115 | |||
116 | ncp_invalidate_conn(server); | ||
117 | del_timer(&server->timeout_tm); | ||
118 | while (!list_empty(&server->tx.requests)) { | ||
119 | req = list_entry(server->tx.requests.next, struct ncp_request_reply, req); | ||
120 | |||
121 | list_del_init(&req->req); | ||
122 | if (req == aborted) { | ||
123 | ncp_finish_request(req, err); | ||
124 | } else { | ||
125 | ncp_finish_request(req, -EIO); | ||
126 | } | ||
127 | } | ||
128 | req = server->rcv.creq; | ||
129 | if (req) { | ||
130 | server->rcv.creq = NULL; | ||
131 | if (req == aborted) { | ||
132 | ncp_finish_request(req, err); | ||
133 | } else { | ||
134 | ncp_finish_request(req, -EIO); | ||
135 | } | ||
136 | server->rcv.ptr = NULL; | ||
137 | server->rcv.state = 0; | ||
138 | } | ||
139 | req = server->tx.creq; | ||
140 | if (req) { | ||
141 | server->tx.creq = NULL; | ||
142 | if (req == aborted) { | ||
143 | ncp_finish_request(req, err); | ||
144 | } else { | ||
145 | ncp_finish_request(req, -EIO); | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | |||
150 | static inline int get_conn_number(struct ncp_reply_header *rp) | ||
151 | { | ||
152 | return rp->conn_low | (rp->conn_high << 8); | ||
153 | } | ||
154 | |||
155 | static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) | ||
156 | { | ||
157 | /* If req is done, we got signal, but we also received answer... */ | ||
158 | switch (req->status) { | ||
159 | case RQ_IDLE: | ||
160 | case RQ_DONE: | ||
161 | break; | ||
162 | case RQ_QUEUED: | ||
163 | list_del_init(&req->req); | ||
164 | ncp_finish_request(req, err); | ||
165 | break; | ||
166 | case RQ_INPROGRESS: | ||
167 | __abort_ncp_connection(server, req, err); | ||
168 | break; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) | ||
173 | { | ||
174 | down(&server->rcv.creq_sem); | ||
175 | __ncp_abort_request(server, req, err); | ||
176 | up(&server->rcv.creq_sem); | ||
177 | } | ||
178 | |||
179 | static inline void __ncptcp_abort(struct ncp_server *server) | ||
180 | { | ||
181 | __abort_ncp_connection(server, NULL, 0); | ||
182 | } | ||
183 | |||
184 | static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req) | ||
185 | { | ||
186 | struct kvec vec[3]; | ||
187 | /* sock_sendmsg updates iov pointers for us :-( */ | ||
188 | memcpy(vec, req->tx_ciov, req->tx_iovlen * sizeof(vec[0])); | ||
189 | return do_send(sock, vec, req->tx_iovlen, | ||
190 | req->tx_totallen, MSG_DONTWAIT); | ||
191 | } | ||
192 | |||
193 | static void __ncptcp_try_send(struct ncp_server *server) | ||
194 | { | ||
195 | struct ncp_request_reply *rq; | ||
196 | struct kvec *iov; | ||
197 | struct kvec iovc[3]; | ||
198 | int result; | ||
199 | |||
200 | rq = server->tx.creq; | ||
201 | if (!rq) | ||
202 | return; | ||
203 | |||
204 | /* sock_sendmsg updates iov pointers for us :-( */ | ||
205 | memcpy(iovc, rq->tx_ciov, rq->tx_iovlen * sizeof(iov[0])); | ||
206 | result = do_send(server->ncp_sock, iovc, rq->tx_iovlen, | ||
207 | rq->tx_totallen, MSG_NOSIGNAL | MSG_DONTWAIT); | ||
208 | |||
209 | if (result == -EAGAIN) | ||
210 | return; | ||
211 | |||
212 | if (result < 0) { | ||
213 | printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result); | ||
214 | __ncp_abort_request(server, rq, result); | ||
215 | return; | ||
216 | } | ||
217 | if (result >= rq->tx_totallen) { | ||
218 | server->rcv.creq = rq; | ||
219 | server->tx.creq = NULL; | ||
220 | return; | ||
221 | } | ||
222 | rq->tx_totallen -= result; | ||
223 | iov = rq->tx_ciov; | ||
224 | while (iov->iov_len <= result) { | ||
225 | result -= iov->iov_len; | ||
226 | iov++; | ||
227 | rq->tx_iovlen--; | ||
228 | } | ||
229 | iov->iov_base += result; | ||
230 | iov->iov_len -= result; | ||
231 | rq->tx_ciov = iov; | ||
232 | } | ||
233 | |||
234 | static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h) | ||
235 | { | ||
236 | req->status = RQ_INPROGRESS; | ||
237 | h->conn_low = server->connection; | ||
238 | h->conn_high = server->connection >> 8; | ||
239 | h->sequence = ++server->sequence; | ||
240 | } | ||
241 | |||
242 | static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req) | ||
243 | { | ||
244 | size_t signlen; | ||
245 | struct ncp_request_header* h; | ||
246 | |||
247 | req->tx_ciov = req->tx_iov + 1; | ||
248 | |||
249 | h = req->tx_iov[1].iov_base; | ||
250 | ncp_init_header(server, req, h); | ||
251 | signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1, | ||
252 | req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1, | ||
253 | cpu_to_le32(req->tx_totallen), req->sign); | ||
254 | if (signlen) { | ||
255 | req->tx_ciov[1].iov_base = req->sign; | ||
256 | req->tx_ciov[1].iov_len = signlen; | ||
257 | req->tx_iovlen += 1; | ||
258 | req->tx_totallen += signlen; | ||
259 | } | ||
260 | server->rcv.creq = req; | ||
261 | server->timeout_last = server->m.time_out; | ||
262 | server->timeout_retries = server->m.retry_count; | ||
263 | ncpdgram_send(server->ncp_sock, req); | ||
264 | mod_timer(&server->timeout_tm, jiffies + server->m.time_out); | ||
265 | } | ||
266 | |||
267 | #define NCP_TCP_XMIT_MAGIC (0x446D6454) | ||
268 | #define NCP_TCP_XMIT_VERSION (1) | ||
269 | #define NCP_TCP_RCVD_MAGIC (0x744E6350) | ||
270 | |||
271 | static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req) | ||
272 | { | ||
273 | size_t signlen; | ||
274 | struct ncp_request_header* h; | ||
275 | |||
276 | req->tx_ciov = req->tx_iov; | ||
277 | h = req->tx_iov[1].iov_base; | ||
278 | ncp_init_header(server, req, h); | ||
279 | signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1, | ||
280 | req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1, | ||
281 | cpu_to_be32(req->tx_totallen + 24), req->sign + 4) + 16; | ||
282 | |||
283 | req->sign[0] = htonl(NCP_TCP_XMIT_MAGIC); | ||
284 | req->sign[1] = htonl(req->tx_totallen + signlen); | ||
285 | req->sign[2] = htonl(NCP_TCP_XMIT_VERSION); | ||
286 | req->sign[3] = htonl(req->datalen + 8); | ||
287 | req->tx_iov[0].iov_base = req->sign; | ||
288 | req->tx_iov[0].iov_len = signlen; | ||
289 | req->tx_iovlen += 1; | ||
290 | req->tx_totallen += signlen; | ||
291 | |||
292 | server->tx.creq = req; | ||
293 | __ncptcp_try_send(server); | ||
294 | } | ||
295 | |||
296 | static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req) | ||
297 | { | ||
298 | if (server->ncp_sock->type == SOCK_STREAM) | ||
299 | ncptcp_start_request(server, req); | ||
300 | else | ||
301 | ncpdgram_start_request(server, req); | ||
302 | } | ||
303 | |||
304 | static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply *req) | ||
305 | { | ||
306 | down(&server->rcv.creq_sem); | ||
307 | if (!ncp_conn_valid(server)) { | ||
308 | up(&server->rcv.creq_sem); | ||
309 | printk(KERN_ERR "ncpfs: tcp: Server died\n"); | ||
310 | return -EIO; | ||
311 | } | ||
312 | if (server->tx.creq || server->rcv.creq) { | ||
313 | req->status = RQ_QUEUED; | ||
314 | list_add_tail(&req->req, &server->tx.requests); | ||
315 | up(&server->rcv.creq_sem); | ||
316 | return 0; | ||
317 | } | ||
318 | __ncp_start_request(server, req); | ||
319 | up(&server->rcv.creq_sem); | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static void __ncp_next_request(struct ncp_server *server) | ||
324 | { | ||
325 | struct ncp_request_reply *req; | ||
326 | |||
327 | server->rcv.creq = NULL; | ||
328 | if (list_empty(&server->tx.requests)) { | ||
329 | return; | ||
330 | } | ||
331 | req = list_entry(server->tx.requests.next, struct ncp_request_reply, req); | ||
332 | list_del_init(&req->req); | ||
333 | __ncp_start_request(server, req); | ||
334 | } | ||
335 | |||
336 | static void info_server(struct ncp_server *server, unsigned int id, const void * data, size_t len) | ||
337 | { | ||
338 | if (server->info_sock) { | ||
339 | struct kvec iov[2]; | ||
340 | __be32 hdr[2]; | ||
341 | |||
342 | hdr[0] = cpu_to_be32(len + 8); | ||
343 | hdr[1] = cpu_to_be32(id); | ||
344 | |||
345 | iov[0].iov_base = hdr; | ||
346 | iov[0].iov_len = 8; | ||
347 | iov[1].iov_base = (void *) data; | ||
348 | iov[1].iov_len = len; | ||
349 | |||
350 | do_send(server->info_sock, iov, 2, len + 8, MSG_NOSIGNAL); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | void ncpdgram_rcv_proc(void *s) | ||
355 | { | ||
356 | struct ncp_server *server = s; | ||
357 | struct socket* sock; | ||
358 | |||
359 | sock = server->ncp_sock; | ||
360 | |||
361 | while (1) { | ||
362 | struct ncp_reply_header reply; | ||
363 | int result; | ||
364 | |||
365 | result = _recv(sock, &reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT); | ||
366 | if (result < 0) { | ||
367 | break; | ||
368 | } | ||
369 | if (result >= sizeof(reply)) { | ||
370 | struct ncp_request_reply *req; | ||
371 | |||
372 | if (reply.type == NCP_WATCHDOG) { | ||
373 | unsigned char buf[10]; | ||
374 | |||
375 | if (server->connection != get_conn_number(&reply)) { | ||
376 | goto drop; | ||
377 | } | ||
378 | result = _recv(sock, buf, sizeof(buf), MSG_DONTWAIT); | ||
379 | if (result < 0) { | ||
380 | DPRINTK("recv failed with %d\n", result); | ||
381 | continue; | ||
382 | } | ||
383 | if (result < 10) { | ||
384 | DPRINTK("too short (%u) watchdog packet\n", result); | ||
385 | continue; | ||
386 | } | ||
387 | if (buf[9] != '?') { | ||
388 | DPRINTK("bad signature (%02X) in watchdog packet\n", buf[9]); | ||
389 | continue; | ||
390 | } | ||
391 | buf[9] = 'Y'; | ||
392 | _send(sock, buf, sizeof(buf)); | ||
393 | continue; | ||
394 | } | ||
395 | if (reply.type != NCP_POSITIVE_ACK && reply.type != NCP_REPLY) { | ||
396 | result = _recv(sock, server->unexpected_packet.data, sizeof(server->unexpected_packet.data), MSG_DONTWAIT); | ||
397 | if (result < 0) { | ||
398 | continue; | ||
399 | } | ||
400 | info_server(server, 0, server->unexpected_packet.data, result); | ||
401 | continue; | ||
402 | } | ||
403 | down(&server->rcv.creq_sem); | ||
404 | req = server->rcv.creq; | ||
405 | if (req && (req->tx_type == NCP_ALLOC_SLOT_REQUEST || (server->sequence == reply.sequence && | ||
406 | server->connection == get_conn_number(&reply)))) { | ||
407 | if (reply.type == NCP_POSITIVE_ACK) { | ||
408 | server->timeout_retries = server->m.retry_count; | ||
409 | server->timeout_last = NCP_MAX_RPC_TIMEOUT; | ||
410 | mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT); | ||
411 | } else if (reply.type == NCP_REPLY) { | ||
412 | result = _recv(sock, (void*)req->reply_buf, req->datalen, MSG_DONTWAIT); | ||
413 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
414 | if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { | ||
415 | if (result < 8 + 8) { | ||
416 | result = -EIO; | ||
417 | } else { | ||
418 | unsigned int hdrl; | ||
419 | |||
420 | result -= 8; | ||
421 | hdrl = sock->sk->sk_family == AF_INET ? 8 : 6; | ||
422 | if (sign_verify_reply(server, ((char*)req->reply_buf) + hdrl, result - hdrl, cpu_to_le32(result), ((char*)req->reply_buf) + result)) { | ||
423 | printk(KERN_INFO "ncpfs: Signature violation\n"); | ||
424 | result = -EIO; | ||
425 | } | ||
426 | } | ||
427 | } | ||
428 | #endif | ||
429 | del_timer(&server->timeout_tm); | ||
430 | server->rcv.creq = NULL; | ||
431 | ncp_finish_request(req, result); | ||
432 | __ncp_next_request(server); | ||
433 | up(&server->rcv.creq_sem); | ||
434 | continue; | ||
435 | } | ||
436 | } | ||
437 | up(&server->rcv.creq_sem); | ||
438 | } | ||
439 | drop:; | ||
440 | _recv(sock, &reply, sizeof(reply), MSG_DONTWAIT); | ||
441 | } | ||
442 | } | ||
443 | |||
444 | static void __ncpdgram_timeout_proc(struct ncp_server *server) | ||
445 | { | ||
446 | /* If timer is pending, we are processing another request... */ | ||
447 | if (!timer_pending(&server->timeout_tm)) { | ||
448 | struct ncp_request_reply* req; | ||
449 | |||
450 | req = server->rcv.creq; | ||
451 | if (req) { | ||
452 | int timeout; | ||
453 | |||
454 | if (server->m.flags & NCP_MOUNT_SOFT) { | ||
455 | if (server->timeout_retries-- == 0) { | ||
456 | __ncp_abort_request(server, req, -ETIMEDOUT); | ||
457 | return; | ||
458 | } | ||
459 | } | ||
460 | /* Ignore errors */ | ||
461 | ncpdgram_send(server->ncp_sock, req); | ||
462 | timeout = server->timeout_last << 1; | ||
463 | if (timeout > NCP_MAX_RPC_TIMEOUT) { | ||
464 | timeout = NCP_MAX_RPC_TIMEOUT; | ||
465 | } | ||
466 | server->timeout_last = timeout; | ||
467 | mod_timer(&server->timeout_tm, jiffies + timeout); | ||
468 | } | ||
469 | } | ||
470 | } | ||
471 | |||
472 | void ncpdgram_timeout_proc(void *s) | ||
473 | { | ||
474 | struct ncp_server *server = s; | ||
475 | down(&server->rcv.creq_sem); | ||
476 | __ncpdgram_timeout_proc(server); | ||
477 | up(&server->rcv.creq_sem); | ||
478 | } | ||
479 | |||
480 | static inline void ncp_init_req(struct ncp_request_reply* req) | ||
481 | { | ||
482 | init_waitqueue_head(&req->wq); | ||
483 | req->status = RQ_IDLE; | ||
484 | } | ||
485 | |||
486 | static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) | ||
487 | { | ||
488 | int result; | ||
489 | |||
490 | if (buffer) { | ||
491 | result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT); | ||
492 | } else { | ||
493 | static unsigned char dummy[1024]; | ||
494 | |||
495 | if (len > sizeof(dummy)) { | ||
496 | len = sizeof(dummy); | ||
497 | } | ||
498 | result = _recv(server->ncp_sock, dummy, len, MSG_DONTWAIT); | ||
499 | } | ||
500 | if (result < 0) { | ||
501 | return result; | ||
502 | } | ||
503 | if (result > len) { | ||
504 | printk(KERN_ERR "ncpfs: tcp: bug in recvmsg (%u > %Zu)\n", result, len); | ||
505 | return -EIO; | ||
506 | } | ||
507 | return result; | ||
508 | } | ||
509 | |||
510 | static int __ncptcp_rcv_proc(struct ncp_server *server) | ||
511 | { | ||
512 | /* We have to check the result, so store the complete header */ | ||
513 | while (1) { | ||
514 | int result; | ||
515 | struct ncp_request_reply *req; | ||
516 | int datalen; | ||
517 | int type; | ||
518 | |||
519 | while (server->rcv.len) { | ||
520 | result = do_tcp_rcv(server, server->rcv.ptr, server->rcv.len); | ||
521 | if (result == -EAGAIN) { | ||
522 | return 0; | ||
523 | } | ||
524 | if (result <= 0) { | ||
525 | req = server->rcv.creq; | ||
526 | if (req) { | ||
527 | __ncp_abort_request(server, req, -EIO); | ||
528 | } else { | ||
529 | __ncptcp_abort(server); | ||
530 | } | ||
531 | if (result < 0) { | ||
532 | printk(KERN_ERR "ncpfs: tcp: error in recvmsg: %d\n", result); | ||
533 | } else { | ||
534 | DPRINTK(KERN_ERR "ncpfs: tcp: EOF\n"); | ||
535 | } | ||
536 | return -EIO; | ||
537 | } | ||
538 | if (server->rcv.ptr) { | ||
539 | server->rcv.ptr += result; | ||
540 | } | ||
541 | server->rcv.len -= result; | ||
542 | } | ||
543 | switch (server->rcv.state) { | ||
544 | case 0: | ||
545 | if (server->rcv.buf.magic != htonl(NCP_TCP_RCVD_MAGIC)) { | ||
546 | printk(KERN_ERR "ncpfs: tcp: Unexpected reply type %08X\n", ntohl(server->rcv.buf.magic)); | ||
547 | __ncptcp_abort(server); | ||
548 | return -EIO; | ||
549 | } | ||
550 | datalen = ntohl(server->rcv.buf.len) & 0x0FFFFFFF; | ||
551 | if (datalen < 10) { | ||
552 | printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen); | ||
553 | __ncptcp_abort(server); | ||
554 | return -EIO; | ||
555 | } | ||
556 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
557 | if (server->sign_active) { | ||
558 | if (datalen < 18) { | ||
559 | printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen); | ||
560 | __ncptcp_abort(server); | ||
561 | return -EIO; | ||
562 | } | ||
563 | server->rcv.buf.len = datalen - 8; | ||
564 | server->rcv.ptr = (unsigned char*)&server->rcv.buf.p1; | ||
565 | server->rcv.len = 8; | ||
566 | server->rcv.state = 4; | ||
567 | break; | ||
568 | } | ||
569 | #endif | ||
570 | type = ntohs(server->rcv.buf.type); | ||
571 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
572 | cont:; | ||
573 | #endif | ||
574 | if (type != NCP_REPLY) { | ||
575 | if (datalen - 8 <= sizeof(server->unexpected_packet.data)) { | ||
576 | *(__u16*)(server->unexpected_packet.data) = htons(type); | ||
577 | server->unexpected_packet.len = datalen - 8; | ||
578 | |||
579 | server->rcv.state = 5; | ||
580 | server->rcv.ptr = server->unexpected_packet.data + 2; | ||
581 | server->rcv.len = datalen - 10; | ||
582 | break; | ||
583 | } | ||
584 | DPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", type); | ||
585 | skipdata2:; | ||
586 | server->rcv.state = 2; | ||
587 | skipdata:; | ||
588 | server->rcv.ptr = NULL; | ||
589 | server->rcv.len = datalen - 10; | ||
590 | break; | ||
591 | } | ||
592 | req = server->rcv.creq; | ||
593 | if (!req) { | ||
594 | DPRINTK(KERN_ERR "ncpfs: Reply without appropriate request\n"); | ||
595 | goto skipdata2; | ||
596 | } | ||
597 | if (datalen > req->datalen + 8) { | ||
598 | printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d (expected at most %Zd)\n", datalen, req->datalen + 8); | ||
599 | server->rcv.state = 3; | ||
600 | goto skipdata; | ||
601 | } | ||
602 | req->datalen = datalen - 8; | ||
603 | req->reply_buf->type = NCP_REPLY; | ||
604 | server->rcv.ptr = (unsigned char*)(req->reply_buf) + 2; | ||
605 | server->rcv.len = datalen - 10; | ||
606 | server->rcv.state = 1; | ||
607 | break; | ||
608 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
609 | case 4: | ||
610 | datalen = server->rcv.buf.len; | ||
611 | type = ntohs(server->rcv.buf.type2); | ||
612 | goto cont; | ||
613 | #endif | ||
614 | case 1: | ||
615 | req = server->rcv.creq; | ||
616 | if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) { | ||
617 | if (req->reply_buf->sequence != server->sequence) { | ||
618 | printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n"); | ||
619 | __ncp_abort_request(server, req, -EIO); | ||
620 | return -EIO; | ||
621 | } | ||
622 | if ((req->reply_buf->conn_low | (req->reply_buf->conn_high << 8)) != server->connection) { | ||
623 | printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n"); | ||
624 | __ncp_abort_request(server, req, -EIO); | ||
625 | return -EIO; | ||
626 | } | ||
627 | } | ||
628 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | ||
629 | if (server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { | ||
630 | if (sign_verify_reply(server, (unsigned char*)(req->reply_buf) + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) { | ||
631 | printk(KERN_ERR "ncpfs: tcp: Signature violation\n"); | ||
632 | __ncp_abort_request(server, req, -EIO); | ||
633 | return -EIO; | ||
634 | } | ||
635 | } | ||
636 | #endif | ||
637 | ncp_finish_request(req, req->datalen); | ||
638 | nextreq:; | ||
639 | __ncp_next_request(server); | ||
640 | case 2: | ||
641 | next:; | ||
642 | server->rcv.ptr = (unsigned char*)&server->rcv.buf; | ||
643 | server->rcv.len = 10; | ||
644 | server->rcv.state = 0; | ||
645 | break; | ||
646 | case 3: | ||
647 | ncp_finish_request(server->rcv.creq, -EIO); | ||
648 | goto nextreq; | ||
649 | case 5: | ||
650 | info_server(server, 0, server->unexpected_packet.data, server->unexpected_packet.len); | ||
651 | goto next; | ||
652 | } | ||
653 | } | ||
654 | } | ||
655 | |||
656 | void ncp_tcp_rcv_proc(void *s) | ||
657 | { | ||
658 | struct ncp_server *server = s; | ||
659 | |||
660 | down(&server->rcv.creq_sem); | ||
661 | __ncptcp_rcv_proc(server); | ||
662 | up(&server->rcv.creq_sem); | ||
663 | } | ||
664 | |||
665 | void ncp_tcp_tx_proc(void *s) | ||
666 | { | ||
667 | struct ncp_server *server = s; | ||
668 | |||
669 | down(&server->rcv.creq_sem); | ||
670 | __ncptcp_try_send(server); | ||
671 | up(&server->rcv.creq_sem); | ||
672 | } | ||
673 | |||
674 | static int do_ncp_rpc_call(struct ncp_server *server, int size, | ||
675 | struct ncp_reply_header* reply_buf, int max_reply_size) | ||
676 | { | ||
677 | int result; | ||
678 | struct ncp_request_reply req; | ||
679 | |||
680 | ncp_init_req(&req); | ||
681 | req.reply_buf = reply_buf; | ||
682 | req.datalen = max_reply_size; | ||
683 | req.tx_iov[1].iov_base = server->packet; | ||
684 | req.tx_iov[1].iov_len = size; | ||
685 | req.tx_iovlen = 1; | ||
686 | req.tx_totallen = size; | ||
687 | req.tx_type = *(u_int16_t*)server->packet; | ||
688 | |||
689 | result = ncp_add_request(server, &req); | ||
690 | if (result < 0) { | ||
691 | return result; | ||
692 | } | ||
693 | if (wait_event_interruptible(req.wq, req.status == RQ_DONE)) { | ||
694 | ncp_abort_request(server, &req, -EIO); | ||
695 | } | ||
696 | return req.result; | ||
697 | } | ||
698 | |||
699 | /* | ||
700 | * We need the server to be locked here, so check! | ||
701 | */ | ||
702 | |||
703 | static int ncp_do_request(struct ncp_server *server, int size, | ||
704 | void* reply, int max_reply_size) | ||
705 | { | ||
706 | int result; | ||
707 | |||
708 | if (server->lock == 0) { | ||
709 | printk(KERN_ERR "ncpfs: Server not locked!\n"); | ||
710 | return -EIO; | ||
711 | } | ||
712 | if (!ncp_conn_valid(server)) { | ||
713 | printk(KERN_ERR "ncpfs: Connection invalid!\n"); | ||
714 | return -EIO; | ||
715 | } | ||
716 | { | ||
717 | sigset_t old_set; | ||
718 | unsigned long mask, flags; | ||
719 | |||
720 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
721 | old_set = current->blocked; | ||
722 | if (current->flags & PF_EXITING) | ||
723 | mask = 0; | ||
724 | else | ||
725 | mask = sigmask(SIGKILL); | ||
726 | if (server->m.flags & NCP_MOUNT_INTR) { | ||
727 | /* FIXME: This doesn't seem right at all. So, like, | ||
728 | we can't handle SIGINT and get whatever to stop? | ||
729 | What if we've blocked it ourselves? What about | ||
730 | alarms? Why, in fact, are we mucking with the | ||
731 | sigmask at all? -- r~ */ | ||
732 | if (current->sighand->action[SIGINT - 1].sa.sa_handler == SIG_DFL) | ||
733 | mask |= sigmask(SIGINT); | ||
734 | if (current->sighand->action[SIGQUIT - 1].sa.sa_handler == SIG_DFL) | ||
735 | mask |= sigmask(SIGQUIT); | ||
736 | } | ||
737 | siginitsetinv(¤t->blocked, mask); | ||
738 | recalc_sigpending(); | ||
739 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
740 | |||
741 | result = do_ncp_rpc_call(server, size, reply, max_reply_size); | ||
742 | |||
743 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
744 | current->blocked = old_set; | ||
745 | recalc_sigpending(); | ||
746 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
747 | } | ||
748 | |||
749 | DDPRINTK("do_ncp_rpc_call returned %d\n", result); | ||
750 | |||
751 | if (result < 0) { | ||
752 | /* There was a problem with I/O, so the connections is | ||
753 | * no longer usable. */ | ||
754 | ncp_invalidate_conn(server); | ||
755 | } | ||
756 | return result; | ||
757 | } | ||
758 | |||
759 | /* ncp_do_request assures that at least a complete reply header is | ||
760 | * received. It assumes that server->current_size contains the ncp | ||
761 | * request size | ||
762 | */ | ||
763 | int ncp_request2(struct ncp_server *server, int function, | ||
764 | void* rpl, int size) | ||
765 | { | ||
766 | struct ncp_request_header *h; | ||
767 | struct ncp_reply_header* reply = rpl; | ||
768 | int result; | ||
769 | |||
770 | h = (struct ncp_request_header *) (server->packet); | ||
771 | if (server->has_subfunction != 0) { | ||
772 | *(__u16 *) & (h->data[0]) = htons(server->current_size - sizeof(*h) - 2); | ||
773 | } | ||
774 | h->type = NCP_REQUEST; | ||
775 | /* | ||
776 | * The server shouldn't know or care what task is making a | ||
777 | * request, so we always use the same task number. | ||
778 | */ | ||
779 | h->task = 2; /* (current->pid) & 0xff; */ | ||
780 | h->function = function; | ||
781 | |||
782 | result = ncp_do_request(server, server->current_size, reply, size); | ||
783 | if (result < 0) { | ||
784 | DPRINTK("ncp_request_error: %d\n", result); | ||
785 | goto out; | ||
786 | } | ||
787 | server->completion = reply->completion_code; | ||
788 | server->conn_status = reply->connection_state; | ||
789 | server->reply_size = result; | ||
790 | server->ncp_reply_size = result - sizeof(struct ncp_reply_header); | ||
791 | |||
792 | result = reply->completion_code; | ||
793 | |||
794 | if (result != 0) | ||
795 | PPRINTK("ncp_request: completion code=%x\n", result); | ||
796 | out: | ||
797 | return result; | ||
798 | } | ||
799 | |||
800 | int ncp_connect(struct ncp_server *server) | ||
801 | { | ||
802 | struct ncp_request_header *h; | ||
803 | int result; | ||
804 | |||
805 | server->connection = 0xFFFF; | ||
806 | server->sequence = 255; | ||
807 | |||
808 | h = (struct ncp_request_header *) (server->packet); | ||
809 | h->type = NCP_ALLOC_SLOT_REQUEST; | ||
810 | h->task = 2; /* see above */ | ||
811 | h->function = 0; | ||
812 | |||
813 | result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); | ||
814 | if (result < 0) | ||
815 | goto out; | ||
816 | server->connection = h->conn_low + (h->conn_high * 256); | ||
817 | result = 0; | ||
818 | out: | ||
819 | return result; | ||
820 | } | ||
821 | |||
822 | int ncp_disconnect(struct ncp_server *server) | ||
823 | { | ||
824 | struct ncp_request_header *h; | ||
825 | |||
826 | h = (struct ncp_request_header *) (server->packet); | ||
827 | h->type = NCP_DEALLOC_SLOT_REQUEST; | ||
828 | h->task = 2; /* see above */ | ||
829 | h->function = 0; | ||
830 | |||
831 | return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); | ||
832 | } | ||
833 | |||
834 | void ncp_lock_server(struct ncp_server *server) | ||
835 | { | ||
836 | down(&server->sem); | ||
837 | if (server->lock) | ||
838 | printk(KERN_WARNING "ncp_lock_server: was locked!\n"); | ||
839 | server->lock = 1; | ||
840 | } | ||
841 | |||
842 | void ncp_unlock_server(struct ncp_server *server) | ||
843 | { | ||
844 | if (!server->lock) { | ||
845 | printk(KERN_WARNING "ncp_unlock_server: was not locked!\n"); | ||
846 | return; | ||
847 | } | ||
848 | server->lock = 0; | ||
849 | up(&server->sem); | ||
850 | } | ||
diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c new file mode 100644 index 000000000000..e935f1b34bc2 --- /dev/null +++ b/fs/ncpfs/symlink.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * linux/fs/ncpfs/symlink.c | ||
3 | * | ||
4 | * Code for allowing symbolic links on NCPFS (i.e. NetWare) | ||
5 | * Symbolic links are not supported on native NetWare, so we use an | ||
6 | * infrequently-used flag (Sh) and store a two-word magic header in | ||
7 | * the file to make sure we don't accidentally use a non-link file | ||
8 | * as a link. | ||
9 | * | ||
10 | * When using the NFS namespace, we set the mode to indicate a symlink and | ||
11 | * don't bother with the magic numbers. | ||
12 | * | ||
13 | * from linux/fs/ext2/symlink.c | ||
14 | * | ||
15 | * Copyright (C) 1998-99, Frank A. Vorstenbosch | ||
16 | * | ||
17 | * ncpfs symlink handling code | ||
18 | * NLS support (c) 1999 Petr Vandrovec | ||
19 | * Modified 2000 Ben Harris, University of Cambridge for NFS NS meta-info | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | |||
25 | #include <asm/uaccess.h> | ||
26 | |||
27 | #include <linux/errno.h> | ||
28 | #include <linux/fs.h> | ||
29 | #include <linux/ncp_fs.h> | ||
30 | #include <linux/time.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <linux/stat.h> | ||
33 | #include "ncplib_kernel.h" | ||
34 | |||
35 | |||
36 | /* these magic numbers must appear in the symlink file -- this makes it a bit | ||
37 | more resilient against the magic attributes being set on random files. */ | ||
38 | |||
39 | #define NCP_SYMLINK_MAGIC0 cpu_to_le32(0x6c6d7973) /* "symlnk->" */ | ||
40 | #define NCP_SYMLINK_MAGIC1 cpu_to_le32(0x3e2d6b6e) | ||
41 | |||
42 | /* ----- read a symbolic link ------------------------------------------ */ | ||
43 | |||
44 | static int ncp_symlink_readpage(struct file *file, struct page *page) | ||
45 | { | ||
46 | struct inode *inode = page->mapping->host; | ||
47 | int error, length, len; | ||
48 | char *link, *rawlink; | ||
49 | char *buf = kmap(page); | ||
50 | |||
51 | error = -ENOMEM; | ||
52 | rawlink=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL); | ||
53 | if (!rawlink) | ||
54 | goto fail; | ||
55 | |||
56 | if (ncp_make_open(inode,O_RDONLY)) | ||
57 | goto failEIO; | ||
58 | |||
59 | error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle, | ||
60 | 0,NCP_MAX_SYMLINK_SIZE,rawlink,&length); | ||
61 | |||
62 | ncp_inode_close(inode); | ||
63 | /* Close file handle if no other users... */ | ||
64 | ncp_make_closed(inode); | ||
65 | if (error) | ||
66 | goto failEIO; | ||
67 | |||
68 | if (NCP_FINFO(inode)->flags & NCPI_KLUDGE_SYMLINK) { | ||
69 | if (length<NCP_MIN_SYMLINK_SIZE || | ||
70 | ((__le32 *)rawlink)[0]!=NCP_SYMLINK_MAGIC0 || | ||
71 | ((__le32 *)rawlink)[1]!=NCP_SYMLINK_MAGIC1) | ||
72 | goto failEIO; | ||
73 | link = rawlink + 8; | ||
74 | length -= 8; | ||
75 | } else { | ||
76 | link = rawlink; | ||
77 | } | ||
78 | |||
79 | len = NCP_MAX_SYMLINK_SIZE; | ||
80 | error = ncp_vol2io(NCP_SERVER(inode), buf, &len, link, length, 0); | ||
81 | kfree(rawlink); | ||
82 | if (error) | ||
83 | goto fail; | ||
84 | SetPageUptodate(page); | ||
85 | kunmap(page); | ||
86 | unlock_page(page); | ||
87 | return 0; | ||
88 | |||
89 | failEIO: | ||
90 | error = -EIO; | ||
91 | kfree(rawlink); | ||
92 | fail: | ||
93 | SetPageError(page); | ||
94 | kunmap(page); | ||
95 | unlock_page(page); | ||
96 | return error; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * symlinks can't do much... | ||
101 | */ | ||
102 | struct address_space_operations ncp_symlink_aops = { | ||
103 | .readpage = ncp_symlink_readpage, | ||
104 | }; | ||
105 | |||
106 | /* ----- create a new symbolic link -------------------------------------- */ | ||
107 | |||
108 | int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { | ||
109 | struct inode *inode; | ||
110 | char *rawlink; | ||
111 | int length, err, i, outlen; | ||
112 | int kludge; | ||
113 | int mode; | ||
114 | __le32 attr; | ||
115 | unsigned int hdr; | ||
116 | |||
117 | DPRINTK("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname); | ||
118 | |||
119 | if (ncp_is_nfs_extras(NCP_SERVER(dir), NCP_FINFO(dir)->volNumber)) | ||
120 | kludge = 0; | ||
121 | else | ||
122 | #ifdef CONFIG_NCPFS_EXTRAS | ||
123 | if (NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS) | ||
124 | kludge = 1; | ||
125 | else | ||
126 | #endif | ||
127 | /* EPERM is returned by VFS if symlink procedure does not exist */ | ||
128 | return -EPERM; | ||
129 | |||
130 | rawlink=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_KERNEL); | ||
131 | if (!rawlink) | ||
132 | return -ENOMEM; | ||
133 | |||
134 | if (kludge) { | ||
135 | mode = 0; | ||
136 | attr = aSHARED | aHIDDEN; | ||
137 | ((__le32 *)rawlink)[0]=NCP_SYMLINK_MAGIC0; | ||
138 | ((__le32 *)rawlink)[1]=NCP_SYMLINK_MAGIC1; | ||
139 | hdr = 8; | ||
140 | } else { | ||
141 | mode = S_IFLNK | S_IRWXUGO; | ||
142 | attr = 0; | ||
143 | hdr = 0; | ||
144 | } | ||
145 | |||
146 | length = strlen(symname); | ||
147 | /* map to/from server charset, do not touch upper/lower case as | ||
148 | symlink can point out of ncp filesystem */ | ||
149 | outlen = NCP_MAX_SYMLINK_SIZE - hdr; | ||
150 | err = ncp_io2vol(NCP_SERVER(dir), rawlink + hdr, &outlen, symname, length, 0); | ||
151 | if (err) | ||
152 | goto failfree; | ||
153 | |||
154 | outlen += hdr; | ||
155 | |||
156 | err = -EIO; | ||
157 | if (ncp_create_new(dir,dentry,mode,0,attr)) { | ||
158 | goto failfree; | ||
159 | } | ||
160 | |||
161 | inode=dentry->d_inode; | ||
162 | |||
163 | if (ncp_make_open(inode, O_WRONLY)) | ||
164 | goto failfree; | ||
165 | |||
166 | if (ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, | ||
167 | 0, outlen, rawlink, &i) || i!=outlen) { | ||
168 | goto fail; | ||
169 | } | ||
170 | |||
171 | ncp_inode_close(inode); | ||
172 | ncp_make_closed(inode); | ||
173 | kfree(rawlink); | ||
174 | return 0; | ||
175 | fail:; | ||
176 | ncp_inode_close(inode); | ||
177 | ncp_make_closed(inode); | ||
178 | failfree:; | ||
179 | kfree(rawlink); | ||
180 | return err; | ||
181 | } | ||
182 | |||
183 | /* ----- EOF ----- */ | ||