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