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