summaryrefslogtreecommitdiffstats
path: root/fs/ncpfs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-13 16:29:25 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-13 16:29:25 +0000
commitdb7d4daea91e105e3859cf461d7e53b9b77454b2 (patch)
tree9bb65b95440af09e8aca63abe56970dd3360cc57 /fs/ncpfs
parent9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff)
Merge with Linux 2.2.8.
Diffstat (limited to 'fs/ncpfs')
-rw-r--r--fs/ncpfs/Config.in5
-rw-r--r--fs/ncpfs/Makefile2
-rw-r--r--fs/ncpfs/dir.c397
-rw-r--r--fs/ncpfs/inode.c165
-rw-r--r--fs/ncpfs/ioctl.c65
-rw-r--r--fs/ncpfs/mmap.c2
-rw-r--r--fs/ncpfs/ncplib_kernel.c54
-rw-r--r--fs/ncpfs/ncplib_kernel.h93
-rw-r--r--fs/ncpfs/sock.c2
-rw-r--r--fs/ncpfs/symlink.c212
10 files changed, 759 insertions, 238 deletions
diff --git a/fs/ncpfs/Config.in b/fs/ncpfs/Config.in
index 21a7af854..11a1b1d36 100644
--- a/fs/ncpfs/Config.in
+++ b/fs/ncpfs/Config.in
@@ -6,5 +6,10 @@ bool ' Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING
bool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG
bool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS
bool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS
+if [ "$CONFIG_NCPFS_OS2_NS" = "y" ]; then
+ bool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS
+fi
bool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR
# bool ' NDS interserver authentication support' CONFIG_NCPFS_NDS_DOMAINS
+bool ' Use Native Language Support' CONFIG_NCPFS_NLS
+bool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile
index 109091ef0..2fb40609c 100644
--- a/fs/ncpfs/Makefile
+++ b/fs/ncpfs/Makefile
@@ -9,7 +9,7 @@
O_TARGET := ncpfs.o
O_OBJS := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \
- ncpsign_kernel.o
+ symlink.o ncpsign_kernel.o
M_OBJS := $(O_TARGET)
# If you want debugging output, please uncomment the following line
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
index 9ef53c41a..cacc0d5c5 100644
--- a/fs/ncpfs/dir.c
+++ b/fs/ncpfs/dir.c
@@ -4,6 +4,7 @@
* Copyright (C) 1995, 1996 by Volker Lendecke
* Modified for big endian by J.F. Chadima and David S. Miller
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998 Wolfram Pienkoss for NLS
*
*/
@@ -22,6 +23,7 @@
#include <linux/locks.h>
#include <linux/ncp_fs.h>
+
#include "ncplib_kernel.h"
struct ncp_dirent {
@@ -48,13 +50,16 @@ static ssize_t ncp_dir_read(struct file *, char *, size_t, loff_t *);
static int ncp_readdir(struct file *, void *, filldir_t);
static int ncp_create(struct inode *, struct dentry *, int);
-static int ncp_lookup(struct inode *, struct dentry *);
+static struct dentry *ncp_lookup(struct inode *, struct dentry *);
static int ncp_unlink(struct inode *, struct dentry *);
static int ncp_mkdir(struct inode *, struct dentry *, int);
static int ncp_rmdir(struct inode *, struct dentry *);
static int ncp_rename(struct inode *, struct dentry *,
struct inode *, struct dentry *);
-
+#ifdef CONFIG_NCPFS_EXTRAS
+extern int ncp_symlink(struct inode *, struct dentry *, const char *);
+#endif
+
static struct file_operations ncp_dir_operations =
{
NULL, /* lseek - default */
@@ -77,7 +82,11 @@ struct inode_operations ncp_dir_inode_operations =
ncp_lookup, /* lookup */
NULL, /* link */
ncp_unlink, /* unlink */
+#ifdef CONFIG_NCPFS_EXTRAS
+ ncp_symlink, /* symlink */
+#else
NULL, /* symlink */
+#endif
ncp_mkdir, /* mkdir */
ncp_rmdir, /* rmdir */
NULL, /* mknod */
@@ -103,14 +112,14 @@ ncp_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
/*
* Dentry operations routines
*/
-static int ncp_lookup_validate(struct dentry *);
+static int ncp_lookup_validate(struct dentry *, int);
static int ncp_hash_dentry(struct dentry *, struct qstr *);
static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
static void ncp_delete_dentry(struct dentry *);
struct dentry_operations ncp_dentry_operations =
{
- ncp_lookup_validate, /* d_validate(struct dentry *) */
+ ncp_lookup_validate, /* d_revalidate(struct dentry *, int) */
ncp_hash_dentry, /* d_hash */
ncp_compare_dentry, /* d_compare */
ncp_delete_dentry /* d_delete(struct dentry *) */
@@ -191,14 +200,6 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
}
}
-/* Here we encapsulate the inode number handling that depends upon the
- * mount mode: When we mount a complete server, the memory address of
- * the ncp_inode_info is used as the inode number. When only a single
- * volume is mounted, then the dirEntNum is used as the inode
- * number. As this is unique for the complete volume, this should
- * enable the NFS exportability of a ncpfs-mounted volume.
- */
-
/*
* Generate a unique inode number.
*/
@@ -253,37 +254,30 @@ static int
ncp_force_unlink(struct inode *dir, struct dentry* dentry)
{
int res=0x9c,res2;
- struct iattr ia;
+ struct nw_modify_dos_info info;
+ __u32 old_nwattr;
+ struct inode *inode;
+ memset(&info, 0, sizeof(info));
+
/* remove the Read-Only flag on the NW server */
+ inode = dentry->d_inode;
- memset(&ia,0,sizeof(struct iattr));
- ia.ia_mode = dentry->d_inode->i_mode;
- ia.ia_mode |= NCP_SERVER(dir)->m.file_mode & 0222; /* set write bits */
- ia.ia_valid = ATTR_MODE;
-
- res2=ncp_notify_change(dentry, &ia);
- if (res2)
- {
- goto leave_me;
- }
+ old_nwattr = NCP_FINFO(inode)->nwattr;
+ info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
+ if (res2)
+ goto leave_me;
/* now try again the delete operation */
-
res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
if (res) /* delete failed, set R bit again */
{
- memset(&ia,0,sizeof(struct iattr));
- ia.ia_mode = dentry->d_inode->i_mode;
- ia.ia_mode &= ~(NCP_SERVER(dir)->m.file_mode & 0222); /* clear write bits */
- ia.ia_valid = ATTR_MODE;
-
- res2=ncp_notify_change(dentry, &ia);
- if (res2)
- {
+ info.attributes = old_nwattr;
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
+ if (res2)
goto leave_me;
- }
}
leave_me:
return(res);
@@ -293,68 +287,65 @@ leave_me:
#ifdef CONFIG_NCPFS_STRONG
static int
ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
- struct inode *new_dir, struct dentry* new_dentry, char *_new_name,
- int *done_flag)
+ struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
{
+ struct nw_modify_dos_info info;
int res=0x90,res2;
- struct iattr ia;
+ struct inode *old_inode = old_dentry->d_inode;
+ __u32 old_nwattr = NCP_FINFO(old_inode)->nwattr;
+ __u32 new_nwattr = 0; /* shut compiler warning */
+ int old_nwattr_changed = 0;
+ int new_nwattr_changed = 0;
+ memset(&info, 0, sizeof(info));
+
/* remove the Read-Only flag on the NW server */
- memset(&ia,0,sizeof(struct iattr));
- ia.ia_mode = old_dentry->d_inode->i_mode;
- ia.ia_mode |= NCP_SERVER(old_dir)->m.file_mode & 0222; /* set write bits */
- ia.ia_valid = ATTR_MODE;
-
- res2=ncp_notify_change(old_dentry, &ia);
- if (res2)
- {
- goto leave_me;
- }
-
+ info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
+ if (!res2)
+ old_nwattr_changed = 1;
+ if (new_dentry && new_dentry->d_inode) {
+ new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr;
+ info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
+ if (!res2)
+ new_nwattr_changed = 1;
+ }
/* now try again the rename operation */
- res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
- old_dir, _old_name,
- new_dir, _new_name);
-
- if (!res) {
- ncp_invalid_dir_cache(old_dir);
- ncp_invalid_dir_cache(new_dir);
- d_move(old_dentry,new_dentry);
- *done_flag=1;
-
- if (!old_dentry->d_inode) {
- DPRINTK(KERN_INFO "ncpfs: no inode -- file remains rw\n");
- goto leave_me;
- }
- if ((res2=ncp_lookup_validate(old_dentry))) {
- DPRINTK(KERN_DEBUG "ncpfs: ncp_lookup_validate returned %d\n",res2);
- }
- }
-
- memset(&ia,0,sizeof(struct iattr));
- ia.ia_mode = old_dentry->d_inode->i_mode;
- ia.ia_mode &= ~(NCP_SERVER(old_dentry->d_inode)->m.file_mode & 0222); /* clear write bits */
- ia.ia_valid = ATTR_MODE;
-
- DPRINTK(KERN_INFO "calling ncp_notify_change() with %s/%s\n",
- old_dentry->d_parent->d_name.name,old_dentry->d_name.name);
-
- res2=ncp_notify_change(old_dentry, &ia);
- if (res2)
- {
- printk(KERN_INFO "ncpfs: ncp_notify_change (2) failed: %08x\n",res2);
- /* goto leave_me; */
- }
-
- leave_me:
+ /* but only if something really happened */
+ if (new_nwattr_changed || old_nwattr_changed) {
+ res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+ old_dir, _old_name,
+ new_dir, _new_name);
+ }
+ if (res)
+ goto leave_me;
+ /* file was successfully renamed, so:
+ do not set attributes on old file - it no longer exists
+ copy attributes from old file to new */
+ new_nwattr_changed = old_nwattr_changed;
+ new_nwattr = old_nwattr;
+ old_nwattr_changed = 0;
+
+leave_me:;
+ if (old_nwattr_changed) {
+ info.attributes = old_nwattr;
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
+ /* ignore errors */
+ }
+ if (new_nwattr_changed) {
+ info.attributes = new_nwattr;
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
+ /* ignore errors */
+ }
return(res);
}
#endif /* CONFIG_NCPFS_STRONG */
static int
-ncp_lookup_validate(struct dentry * dentry)
+ncp_lookup_validate(struct dentry * dentry, int flags)
{
struct ncp_server *server;
struct inode *dir = dentry->d_parent->d_inode;
@@ -384,11 +375,6 @@ ncp_lookup_validate(struct dentry * dentry)
printk(KERN_DEBUG "ncp_lookup_validate: %s, len %d\n", __name, len);
#endif
- if (!ncp_preserve_case(dir)) {
- str_lower(__name);
- down_case = 1;
- }
-
/* If the file is in the dir cache, we do not have to ask the
server. */
@@ -398,17 +384,14 @@ dentry->d_parent->d_name.name, __name);
#endif
if (ncp_is_server_root(dir))
{
- str_upper(__name);
+ io2vol(server, __name, 1);
down_case = 1;
res = ncp_lookup_volume(server, __name,
&(finfo.nw_info.i));
} else
{
- if (!ncp_preserve_case(dir))
- {
- str_upper(__name);
- down_case = 1;
- }
+ down_case = !ncp_preserve_case(dir);
+ io2vol(server, __name, down_case);
res = ncp_obtain_info(server, dir, __name,
&(finfo.nw_info.i));
}
@@ -427,6 +410,9 @@ dentry->d_parent->d_name.name, __name, res);
else
printk(KERN_DEBUG "ncp_lookup_validate: found, but dirEntNum changed\n");
#endif
+ vol2io(server, finfo.nw_info.i.entryName,
+ !ncp_preserve_entry_case(dir,
+ finfo.nw_info.i.NSCreator));
ncp_update_inode2(dentry->d_inode, &finfo.nw_info);
}
if (!val) ncp_invalid_dir_cache(dir);
@@ -454,11 +440,6 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
DDPRINTK(KERN_DEBUG "ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n",
inode->i_ino, c_ino);
- result = -EBADF;
- if (!inode || !S_ISDIR(inode->i_mode)) {
- printk(KERN_WARNING "ncp_readdir: inode is NULL or not a directory\n");
- goto out;
- }
result = -EIO;
if (!ncp_conn_valid(server))
goto out;
@@ -533,10 +514,11 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
c_last_returned_index = 0;
index = 0;
- if (!ncp_preserve_case(inode)) {
- for (i = 0; i < c_size; i++) {
- str_lower(c_entry[i].i.entryName);
- }
+ for (i = 0; i < c_size; i++)
+ {
+ vol2io(server, c_entry[i].i.entryName,
+ !ncp_preserve_entry_case(inode,
+ c_entry[i].i.NSCreator));
}
}
}
@@ -728,7 +710,7 @@ int ncp_conn_logged_in(struct ncp_server *server)
struct dentry* dent;
result = -ENOENT;
- str_upper(server->m.mounted_vol);
+ io2vol(server, server->m.mounted_vol, 1);
if (ncp_lookup_volume(server, server->m.mounted_vol,
&(server->root.finfo.i)) != 0) {
#ifdef NCPFS_PARANOIA
@@ -736,7 +718,7 @@ printk(KERN_DEBUG "ncp_conn_logged_in: %s not found\n", server->m.mounted_vol);
#endif
goto out;
}
- str_lower(server->root.finfo.i.entryName);
+ vol2io(server, server->root.finfo.i.entryName, 1);
dent = server->root_dentry;
if (dent) {
struct inode* ino = dent->d_inode;
@@ -757,7 +739,7 @@ out:
return result;
}
-static int ncp_lookup(struct inode *dir, struct dentry *dentry)
+static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry)
{
struct ncp_server *server;
struct inode *inode = NULL;
@@ -767,11 +749,6 @@ static int ncp_lookup(struct inode *dir, struct dentry *dentry)
struct ncpfs_inode_info finfo;
__u8 __name[dentry->d_name.len + 1];
- error = -ENOENT;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_lookup: inode is NULL or not a directory.\n");
- goto finished;
- }
server = NCP_SERVER(dir);
error = -EIO;
@@ -784,11 +761,6 @@ static int ncp_lookup(struct inode *dir, struct dentry *dentry)
printk(KERN_DEBUG "ncp_lookup: %s, len %d\n", __name, len);
#endif
- if (!ncp_preserve_case(dir)) {
- str_lower(__name);
- down_case = 1;
- }
-
/* If the file is in the dir cache, we do not have to ask the
server. */
@@ -828,17 +800,14 @@ dentry->d_parent->d_name.name, __name);
#endif
if (ncp_is_server_root(dir))
{
- str_upper(__name);
+ io2vol(server, __name, 1);
down_case = 1;
res = ncp_lookup_volume(server, __name,
&(finfo.nw_info.i));
} else
{
- if (!ncp_preserve_case(dir))
- {
- str_upper(__name);
- down_case = 1;
- }
+ down_case = !ncp_preserve_case(dir);
+ io2vol(server, __name, down_case);
res = ncp_obtain_info(server, dir, __name,
&(finfo.nw_info.i));
}
@@ -849,8 +818,11 @@ dentry->d_parent->d_name.name, __name, res);
/*
* If we didn't find an entry, make a negative dentry.
*/
- if (res != 0)
+ if (res != 0) {
goto add_entry;
+ } else vol2io(server, finfo.nw_info.i.entryName,
+ !ncp_preserve_entry_case(dir,
+ finfo.nw_info.i.NSCreator));
}
/*
@@ -872,7 +844,7 @@ finished:
#ifdef NCPFS_PARANOIA
printk(KERN_DEBUG "ncp_lookup: result=%d\n", error);
#endif
- return error;
+ return ERR_PTR(error);
}
/*
@@ -904,20 +876,17 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
}
-static int ncp_create(struct inode *dir, struct dentry *dentry, int mode)
+int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode,
+ int attributes)
{
int error, result;
struct ncpfs_inode_info finfo;
__u8 _name[dentry->d_name.len + 1];
-
+
#ifdef NCPFS_PARANOIA
-printk(KERN_DEBUG "ncp_create: creating %s/%s, mode=%x\n",
+printk(KERN_DEBUG "ncp_create_new: creating %s/%s, mode=%x\n",
dentry->d_parent->d_name.name, dentry->d_name.name, mode);
#endif
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_create: inode is NULL or not a directory\n");
- return -ENOENT;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
goto out;
@@ -925,14 +894,12 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode);
strncpy(_name, dentry->d_name.name, dentry->d_name.len);
_name[dentry->d_name.len] = '\0';
- if (!ncp_preserve_case(dir)) {
- str_upper(_name);
- }
+ io2vol(NCP_SERVER(dir), _name, !ncp_preserve_case(dir));
error = -EACCES;
result = ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name,
OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
- 0, AR_READ | AR_WRITE, &finfo.nw_info);
+ attributes, AR_READ | AR_WRITE, &finfo.nw_info);
if (!result) {
finfo.nw_info.access = O_RDWR;
error = ncp_instantiate(dir, dentry, &finfo);
@@ -946,6 +913,11 @@ out:
return error;
}
+static int ncp_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ return ncp_create_new(dir, dentry, mode, 0);
+}
+
static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
int error;
@@ -954,20 +926,13 @@ static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
DPRINTK(KERN_DEBUG "ncp_mkdir: making %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- error = -ENOTDIR;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_mkdir: inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
goto out;
strncpy(_name, dentry->d_name.name, dentry->d_name.len);
_name[dentry->d_name.len] = '\0';
- if (!ncp_preserve_case(dir)) {
- str_upper(_name);
- }
+ io2vol(NCP_SERVER(dir), _name, !ncp_preserve_case(dir));
error = -EACCES;
if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name,
@@ -987,13 +952,6 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
DPRINTK(KERN_DEBUG "ncp_rmdir: removing %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
-
- error = -ENOENT;
- if (!dir || !S_ISDIR(dir->i_mode))
- {
- printk(KERN_WARNING "ncp_rmdir: inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
@@ -1006,17 +964,34 @@ static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
strncpy(_name, dentry->d_name.name, dentry->d_name.len);
_name[dentry->d_name.len] = '\0';
- if (!ncp_preserve_case(dir))
- {
- str_upper(_name);
- }
- error = -EACCES;
+ io2vol(NCP_SERVER(dir), _name, !ncp_preserve_case(dir));
result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name);
- if (!result)
- {
- ncp_invalid_dir_cache(dir);
- error = 0;
- }
+ switch (result) {
+ case 0x00:
+ ncp_invalid_dir_cache(dir);
+ error = 0;
+ break;
+ case 0x85: /* unauthorized to delete file */
+ case 0x8A: /* unauthorized to delete file */
+ error = -EACCES;
+ break;
+ case 0x8F:
+ case 0x90: /* read only */
+ error = -EPERM;
+ break;
+ case 0x9F: /* in use by another client */
+ error = -EBUSY;
+ break;
+ case 0xA0: /* directory not empty */
+ error = -ENOTEMPTY;
+ break;
+ case 0xFF: /* someone deleted file */
+ error = -ENOENT;
+ break;
+ default:
+ error = -EACCES;
+ break;
+ }
out:
return error;
}
@@ -1029,11 +1004,6 @@ static int ncp_unlink(struct inode *dir, struct dentry *dentry)
DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
- error = -ENOTDIR;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- printk(KERN_WARNING "ncp_unlink: inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(dir)))
goto out;
@@ -1050,19 +1020,38 @@ printk(KERN_DEBUG "ncp_unlink: closing file\n");
error = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
#ifdef CONFIG_NCPFS_STRONG
+ /* 9C is Invalid path.. It should be 8F, 90 - read only, but
+ it is not :-( */
if (error == 0x9C && NCP_SERVER(dir)->m.flags & NCP_MOUNT_STRONG) { /* R/O */
error = ncp_force_unlink(dir, dentry);
}
#endif
- if (!error) {
- DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
- ncp_invalid_dir_cache(dir);
- d_delete(dentry);
- } else if (error == 0xFF) {
- error = -ENOENT;
- } else {
- error = -EACCES;
+ switch (error) {
+ case 0x00:
+ DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ ncp_invalid_dir_cache(dir);
+ d_delete(dentry);
+ break;
+ case 0x85:
+ case 0x8A:
+ error = -EACCES;
+ break;
+ case 0x8D: /* some files in use */
+ case 0x8E: /* all files in use */
+ error = -EBUSY;
+ break;
+ case 0x8F: /* some read only */
+ case 0x90: /* all read only */
+ case 0x9C: /* !!! returned when in-use or read-only by NW4 */
+ error = -EPERM;
+ break;
+ case 0xFF:
+ error = -ENOENT;
+ break;
+ default:
+ error = -EACCES;
+ break;
}
out:
@@ -1074,7 +1063,7 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
{
int old_len = old_dentry->d_name.len;
int new_len = new_dentry->d_name.len;
- int error, done_flag=0;
+ int error;
char _old_name[old_dentry->d_name.len + 1];
char _new_name[new_dentry->d_name.len + 1];
@@ -1082,58 +1071,44 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
- error = -ENOTDIR;
- if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
- printk(KERN_WARNING "ncp_rename: old inode is NULL or not a directory\n");
- goto out;
- }
- if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
- printk(KERN_WARNING "ncp_rename: new inode is NULL or not a directory\n");
- goto out;
- }
error = -EIO;
if (!ncp_conn_valid(NCP_SERVER(old_dir)))
goto out;
strncpy(_old_name, old_dentry->d_name.name, old_len);
_old_name[old_len] = '\0';
- if (!ncp_preserve_case(old_dir)) {
- str_upper(_old_name);
- }
+ io2vol(NCP_SERVER(old_dir), _old_name, !ncp_preserve_case(old_dir));
strncpy(_new_name, new_dentry->d_name.name, new_len);
_new_name[new_len] = '\0';
- if (!ncp_preserve_case(new_dir)) {
- str_upper(_new_name);
- }
+ io2vol(NCP_SERVER(new_dir), _new_name, !ncp_preserve_case(new_dir));
error = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
old_dir, _old_name,
new_dir, _new_name);
#ifdef CONFIG_NCPFS_STRONG
- if (error == 0x90 && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */
+ if ((error == 0x90 || error == -EACCES) && NCP_SERVER(old_dir)->m.flags & NCP_MOUNT_STRONG) { /* RO */
error = ncp_force_rename(old_dir, old_dentry, _old_name,
- new_dir, new_dentry, _new_name,
- &done_flag);
+ new_dir, new_dentry, _new_name);
}
#endif
- if (error == 0)
- {
- if (done_flag == 0) /* if 1, the following already happened */
- { /* in ncp_force_rename() */
- DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n",
+ switch (error) {
+ case 0x00:
+ DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n",
old_dentry->d_name.name,new_dentry->d_name.name);
- ncp_invalid_dir_cache(old_dir);
- ncp_invalid_dir_cache(new_dir);
- d_move(old_dentry,new_dentry);
- }
- } else {
- if (error == 0x9E)
+ ncp_invalid_dir_cache(old_dir);
+ ncp_invalid_dir_cache(new_dir);
+ /* d_move(old_dentry, new_dentry); */
+ break;
+ case 0x9E:
error = -ENAMETOOLONG;
- else if (error == 0xFF)
+ break;
+ case 0xFF:
error = -ENOENT;
- else
+ break;
+ default:
error = -EACCES;
+ break;
}
out:
return error;
@@ -1152,14 +1127,12 @@ extern struct timezone sys_tz;
static int utc2local(int time)
{
- return time - sys_tz.tz_minuteswest * 60 +
- (sys_tz.tz_dsttime ? 3600 : 0);
+ return time - sys_tz.tz_minuteswest * 60;
}
static int local2utc(int time)
{
- return time + sys_tz.tz_minuteswest * 60 -
- (sys_tz.tz_dsttime ? 3600 : 0);
+ return time + sys_tz.tz_minuteswest * 60;
}
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
@@ -1168,7 +1141,9 @@ ncp_date_dos2unix(unsigned short time, unsigned short date)
{
int month, year, secs;
- month = ((date >> 5) & 15) - 1;
+ /* first subtract and mask after that... Otherwise, if
+ date == 0, bad things happen */
+ month = ((date >> 5) - 1) & 15;
year = date >> 9;
secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
86400 * ((date & 31) - 1 + day_n[month] + (year / 4) +
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
index 4a0459cdf..1afee6c7e 100644
--- a/fs/ncpfs/inode.c
+++ b/fs/ncpfs/inode.c
@@ -4,6 +4,7 @@
* Copyright (C) 1995, 1996 by Volker Lendecke
* Modified for big endian by J.F. Chadima and David S. Miller
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998 Wolfram Pienkoss for NLS
*
*/
@@ -27,6 +28,7 @@
#include <linux/init.h>
#include <linux/ncp_fs.h>
+
#include "ncplib_kernel.h"
static void ncp_read_inode(struct inode *);
@@ -49,6 +51,10 @@ static struct super_operations ncp_sops =
};
extern struct dentry_operations ncp_dentry_operations;
+#ifdef CONFIG_NCPFS_EXTRAS
+extern struct inode_operations ncp_symlink_inode_operations;
+extern int ncp_symlink(struct inode*, struct dentry*, const char*);
+#endif
static struct nw_file_info *read_nwinfo = NULL;
static struct semaphore read_sem = MUTEX;
@@ -62,6 +68,12 @@ void ncp_update_inode(struct inode *inode, struct nw_file_info *nwinfo)
NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber;
+#ifdef CONFIG_NCPFS_SMALLDOS
+ NCP_FINFO(inode)->origNS = nwinfo->i.NSCreator;
+#endif
+#ifdef CONFIG_NCPFS_STRONG
+ NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
+#endif
NCP_FINFO(inode)->opened = nwinfo->opened;
NCP_FINFO(inode)->access = nwinfo->access;
NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle;
@@ -79,12 +91,42 @@ void ncp_update_inode2(struct inode* inode, struct nw_file_info *nwinfo)
struct ncp_server *server = NCP_SERVER(inode);
if (!NCP_FINFO(inode)->opened) {
+#ifdef CONFIG_NCPFS_STRONG
+ NCP_FINFO(inode)->nwattr = nwi->attributes;
+#endif
if (nwi->attributes & aDIR) {
inode->i_mode = server->m.dir_mode;
inode->i_size = 512;
} else {
inode->i_mode = server->m.file_mode;
inode->i_size = le32_to_cpu(nwi->dataStreamSize);
+#ifdef CONFIG_NCPFS_EXTRAS
+ if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) && (nwi->attributes & aSHARED)) {
+ switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
+ case aHIDDEN:
+ if (server->m.flags & NCP_MOUNT_SYMLINKS) {
+ if ((inode->i_size >= NCP_MIN_SYMLINK_SIZE)
+ && (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) {
+ inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
+ break;
+ }
+ }
+ /* FALLTHROUGH */
+ case 0:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= 0444;
+ break;
+ case aSYSTEM:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= (inode->i_mode >> 2) & 0111;
+ break;
+ /* case aSYSTEM|aHIDDEN: */
+ default:
+ /* reserved combination */
+ break;
+ }
+ }
+#endif
}
if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
}
@@ -114,6 +156,34 @@ static void ncp_set_attr(struct inode *inode, struct nw_file_info *nwinfo)
} else {
inode->i_mode = server->m.file_mode;
inode->i_size = le32_to_cpu(nwi->dataStreamSize);
+#ifdef CONFIG_NCPFS_EXTRAS
+ if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS))
+ && (nwi->attributes & aSHARED)) {
+ switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
+ case aHIDDEN:
+ if (server->m.flags & NCP_MOUNT_SYMLINKS) {
+ if ((inode->i_size >= NCP_MIN_SYMLINK_SIZE)
+ && (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) {
+ inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
+ break;
+ }
+ }
+ /* FALLTHROUGH */
+ case 0:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= 0444;
+ break;
+ case aSYSTEM:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= (inode->i_mode >> 2) & 0111;
+ break;
+ /* case aSYSTEM|aHIDDEN: */
+ default:
+ /* reserved combination */
+ break;
+ }
+ }
+#endif
}
if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
@@ -157,6 +227,10 @@ static void ncp_read_inode(struct inode *inode)
inode->i_op = &ncp_file_inode_operations;
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &ncp_dir_inode_operations;
+#ifdef CONFIG_NCPFS_EXTRAS
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &ncp_symlink_inode_operations;
+#endif
} else {
inode->i_op = NULL;
}
@@ -211,7 +285,6 @@ static void ncp_init_root(struct ncp_server *server,
{
struct ncp_inode_info *root = &(server->root);
struct nw_info_struct *i = &(root->finfo.i);
- unsigned short dummy;
DPRINTK(KERN_DEBUG "ncp_init_root: i = %x\n", (int) i);
@@ -219,15 +292,13 @@ static void ncp_init_root(struct ncp_server *server,
i->dataStreamSize= 1024;
i->dirEntNum = 0;
i->DosDirNum = 0;
+#ifdef CONFIG_NCPFS_SMALLDOS
+ i->NSCreator = NW_NS_DOS;
+#endif
i->volNumber = NCP_NUMBER_OF_VOLUMES + 1; /* illegal volnum */
- ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate));
- ncp_date_unix2dos(0, &(i->modifyTime ), &(i->modifyDate));
- ncp_date_unix2dos(0, &(dummy ), &(i->lastAccessDate));
- i->creationTime = le16_to_cpu(i->creationTime);
- i->creationDate = le16_to_cpu(i->creationDate);
- i->modifyTime = le16_to_cpu(i->modifyTime);
- i->modifyDate = le16_to_cpu(i->modifyDate);
- i->lastAccessDate= le16_to_cpu(i->lastAccessDate);
+ /* set dates of mountpoint to Jan 1, 1986; 00:00 */
+ i->creationTime = i->modifyTime = cpu_to_le16(0x0000);
+ i->creationDate = i->modifyDate = i->lastAccessDate = cpu_to_le16(0x0C21);
i->nameLen = 0;
i->entryName[0] = '\0';
@@ -308,6 +379,14 @@ ncp_read_super(struct super_block *sb, void *raw_data, int silent)
server->m.dir_mode = (server->m.dir_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
+#ifdef CONFIG_NCPFS_NLS
+ /* load the default NLS charsets */
+ server->nls_charsets.codepage[0] = 0;
+ server->nls_charsets.iocharset[0] = 0;
+ server->nls_vol = load_nls_default();
+ server->nls_io = load_nls_default();
+#endif /* CONFIG_NCPFS_NLS */
+
server->packet_size = NCP_PACKET_SIZE;
server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL);
if (server->packet == NULL)
@@ -377,6 +456,10 @@ out_free_packet:
out_no_packet:
printk(KERN_ERR "ncp_read_super: could not alloc packet\n");
out_free_server:
+#ifdef CONFIG_NCPFS_NLS
+ unload_nls(server->nls_io);
+ unload_nls(server->nls_vol);
+#endif
ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server));
goto out_unlock;
out_no_server:
@@ -416,6 +499,20 @@ static void ncp_put_super(struct super_block *sb)
ncp_disconnect(server);
ncp_unlock_server(server);
+#ifdef CONFIG_NCPFS_NLS
+ /* unload the NLS charsets */
+ if (server->nls_vol)
+ {
+ unload_nls(server->nls_vol);
+ server->nls_vol = NULL;
+ }
+ if (server->nls_io)
+ {
+ unload_nls(server->nls_io);
+ server->nls_io = NULL;
+ }
+#endif /* CONFIG_NCPFS_NLS */
+
fput(server->ncp_filp);
kill_proc(server->m.wdog_pid, SIGTERM, 1);
@@ -457,9 +554,12 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
int result = 0;
int info_mask;
struct nw_modify_dos_info info;
+ struct ncp_server *server;
result = -EIO;
- if (!ncp_conn_valid(NCP_SERVER(inode)))
+
+ server = NCP_SERVER(inode);
+ if ((!server) || !ncp_conn_valid(server))
goto out;
result = inode_change_ok(inode, attr);
@@ -468,11 +568,11 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
result = -EPERM;
if (((attr->ia_valid & ATTR_UID) &&
- (attr->ia_uid != NCP_SERVER(inode)->m.uid)))
+ (attr->ia_uid != server->m.uid)))
goto out;
if (((attr->ia_valid & ATTR_GID) &&
- (attr->ia_uid != NCP_SERVER(inode)->m.gid)))
+ (attr->ia_gid != server->m.gid)))
goto out;
if (((attr->ia_valid & ATTR_MODE) &&
@@ -486,26 +586,53 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
#if 1
if ((attr->ia_valid & ATTR_MODE) != 0)
{
- if (!S_ISREG(inode->i_mode))
+ if (S_ISDIR(inode->i_mode)) {
+ umode_t newmode;
+
+ info_mask |= DM_ATTRIBUTES;
+ newmode = attr->ia_mode;
+ newmode &= NCP_SERVER(inode)->m.dir_mode;
+
+ if (newmode & 0222)
+ info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ else
+ info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ } else if (!S_ISREG(inode->i_mode))
{
return -EPERM;
}
else
{
umode_t newmode;
-
+#ifdef CONFIG_NCPFS_EXTRAS
+ int extras;
+
+ extras = server->m.flags & NCP_MOUNT_EXTRAS;
+#endif
info_mask |= DM_ATTRIBUTES;
newmode=attr->ia_mode;
- newmode &= NCP_SERVER(inode)->m.file_mode;
+#ifdef CONFIG_NCPFS_EXTRAS
+ if (!extras)
+#endif
+ newmode &= server->m.file_mode;
if (newmode & 0222) /* any write bit set */
{
- info.attributes &= ~0x60001;
+ info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
}
else
{
- info.attributes |= 0x60001;
+ info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
}
+#ifdef CONFIG_NCPFS_EXTRAS
+ if (extras) {
+ if (newmode & 0111) /* any execute bit set */
+ info.attributes |= aSHARED | aSYSTEM;
+ /* read for group/world and not in default file_mode */
+ else if (newmode & ~server->m.file_mode & 0444)
+ info.attributes |= aSHARED;
+ }
+#endif
}
}
#endif
@@ -546,6 +673,10 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
result = 0;
}
}
+#ifdef CONFIG_NCPFS_STRONG
+ if ((!result) && (info_mask & DM_ATTRIBUTES))
+ NCP_FINFO(inode)->nwattr = info.attributes;
+#endif
}
if ((attr->ia_valid & ATTR_SIZE) != 0) {
int written;
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
index 2df6fee09..8ada3752b 100644
--- a/fs/ncpfs/ioctl.c
+++ b/fs/ncpfs/ioctl.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 1995, 1996 by Volker Lendecke
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998 Wolfram Pienkoss for NLS
*
*/
@@ -17,6 +18,7 @@
#include <linux/ncp.h>
#include <linux/ncp_fs.h>
+
#include "ncplib_kernel.h"
/* maximum limit for ncp_objectname_ioctl */
@@ -485,6 +487,69 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
return 0;
}
#endif /* CONFIG_NCPFS_NDS_DOMAINS */
+
+#ifdef CONFIG_NCPFS_NLS
+/* Here we are select the iocharset and the codepage for NLS.
+ * Thanks Petr Vandrovec for idea and many hints.
+ */
+ case NCP_IOC_SETCHARSETS:
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ if (server->root_setuped) return -EBUSY;
+ {
+ struct ncp_nls_ioctl user;
+ struct nls_table *codepage;
+ struct nls_table *iocharset;
+ struct nls_table *oldset_io;
+ struct nls_table *oldset_cp;
+
+ if (copy_from_user(&user,
+ (struct ncp_nls_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+
+ codepage = NULL;
+ if (!user.codepage[0]) {
+ codepage = load_nls_default();
+ }
+ else {
+ codepage = load_nls(user.codepage);
+ if (! codepage) {
+ return -EBADRQC;
+ }
+ }
+
+ iocharset = NULL;
+ if (user.iocharset[0] == 0) {
+ iocharset = load_nls_default();
+ }
+ else {
+ iocharset = load_nls(user.iocharset);
+ if (! iocharset) {
+ unload_nls(codepage);
+ return -EBADRQC;
+ }
+ }
+
+ oldset_cp = server->nls_vol;
+ server->nls_vol = codepage;
+ oldset_io = server->nls_io;
+ server->nls_io = iocharset;
+ server->nls_charsets = user;
+ if (oldset_cp) unload_nls(oldset_cp);
+ if (oldset_io) unload_nls(oldset_io);
+ return 0;
+ }
+
+ case NCP_IOC_GETCHARSETS: /* not tested */
+ if (copy_to_user((struct ncp_nls_ioctl*)arg,
+ &(server->nls_charsets),
+ sizeof(server->nls_charsets))) return -EFAULT;
+ return 0;
+#endif /* CONFIG_NCPFS_NLS */
+
default:
return -EINVAL;
}
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
index 2c7610d0b..6b321e6c9 100644
--- a/fs/ncpfs/mmap.c
+++ b/fs/ncpfs/mmap.c
@@ -137,8 +137,6 @@ int ncp_mmap(struct file *file, struct vm_area_struct *vma)
inode->i_atime = CURRENT_TIME;
}
- vma->vm_file = file;
- file->f_count++;
vma->vm_ops = &ncp_file_mmap;
return 0;
}
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
index fc0bbf013..bb034a4e4 100644
--- a/fs/ncpfs/ncplib_kernel.c
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -238,7 +238,8 @@ NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum, err);
}
static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num,
- __u32 dir_base, int have_dir_base, char *path)
+ __u32 dir_base, int have_dir_base,
+ const char *path)
{
ncp_add_byte(server, vol_num);
ncp_add_dword(server, dir_base);
@@ -468,12 +469,17 @@ ncp_lookup_volume(struct ncp_server *server, char *volname,
target->nameLen = strlen(volname);
strcpy(target->entryName, volname);
target->attributes = aDIR;
+ /* set dates to Jan 1, 1986 00:00 */
+ target->creationTime = target->modifyTime = cpu_to_le16(0x0000);
+ target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21);
return 0;
}
-int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
- struct inode *dir, __u32 info_mask,
- struct nw_modify_dos_info *info)
+int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server,
+ struct inode *dir,
+ const char *path,
+ __u32 info_mask,
+ const struct nw_modify_dos_info *info)
{
__u8 volnum = NCP_FINFO(dir)->volNumber;
__u32 dirent = NCP_FINFO(dir)->dirEntNum;
@@ -487,13 +493,22 @@ int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
ncp_add_dword(server, info_mask);
ncp_add_mem(server, info, sizeof(*info));
- ncp_add_handle_path(server, volnum, dirent, 1, NULL);
+ ncp_add_handle_path(server, volnum, dirent, 1, path);
result = ncp_request(server, 87);
ncp_unlock_server(server);
return result;
}
+int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
+ struct inode *dir,
+ __u32 info_mask,
+ const struct nw_modify_dos_info *info)
+{
+ return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL,
+ info_mask, info);
+}
+
static int
ncp_DeleteNSEntry(struct ncp_server *server,
__u8 have_dir_base, __u8 volnum, __u32 dirent,
@@ -788,6 +803,35 @@ out:
return result;
}
+#ifdef CONFIG_NCPFS_EXTRAS
+int
+ncp_read_kernel(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read, char *target, int *bytes_read) {
+ int error;
+ mm_segment_t old_fs;
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ error = ncp_read(server, file_id, offset, to_read, target, bytes_read);
+ set_fs(old_fs);
+ return error;
+}
+
+int
+ncp_write_kernel(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_write,
+ const char *source, int *bytes_written) {
+ int error;
+ mm_segment_t old_fs;
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ error = ncp_write(server, file_id, offset, to_write, source, bytes_written);
+ set_fs(old_fs);
+ return error;
+}
+#endif
+
#ifdef CONFIG_NCPFS_IOCTL_LOCKING
int
ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h
index 7834c6c4f..cc1df1896 100644
--- a/fs/ncpfs/ncplib_kernel.h
+++ b/fs/ncpfs/ncplib_kernel.h
@@ -4,6 +4,7 @@
* Copyright (C) 1995, 1996 by Volker Lendecke
* Modified for big endian by J.F. Chadima and David S. Miller
* Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998 Wolfram Pienkoss for NLS
*
*/
@@ -23,6 +24,10 @@
#include <asm/unaligned.h>
#include <asm/string.h>
+#ifdef CONFIG_NCPFS_NLS
+#include <linux/nls.h>
+#endif
+
#include <linux/ncp.h>
#include <linux/ncp_fs.h>
#include <linux/ncp_fs_sb.h>
@@ -36,12 +41,19 @@ int ncp_close_file(struct ncp_server *, const char *);
int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *);
int ncp_write(struct ncp_server *, const char *, __u32, __u16,
const char *, int *);
+#ifdef CONFIG_NCPFS_EXTRAS
+int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, char *, int *);
+int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16,
+ const char *, int *);
+#endif
int ncp_obtain_info(struct ncp_server *server, struct inode *, char *,
struct nw_info_struct *target);
int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *);
int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *,
- __u32, struct nw_modify_dos_info *info);
+ __u32, const struct nw_modify_dos_info *info);
+int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *, struct inode *,
+ const char* path, __u32, const struct nw_modify_dos_info *info);
int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*);
int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *);
@@ -75,4 +87,83 @@ int
ncp_mount_subdir(struct ncp_server* server, __u8 volNumber,
__u8 srcNS, __u32 srcDirEntNum);
#endif /* CONFIG_NCPFS_MOUNT_SUBDIR */
+
+#ifdef CONFIG_NCPFS_NLS
+/* This are the NLS conversion routines with inspirations and code parts
+ * from the vfat file system and hints from Petr Vandrovec.
+ */
+
+/*
+ * It should be replaced by charset specifc conversion. Gordon Chaffee
+ * has prepared some things, but I don't know, what he thinks about it.
+ * The conversion tables for the io charsets should be generatable by
+ * Unicode table, shouldn't it? I have written so generation code for it.
+ * The tables for the vendor specific codepages...? Hmm. The Samba sources
+ * contains also any hints.
+ */
+
+#define toupperif(c, u) ((((u) != 0) && ((c) >= 'a') && ((c) <= 'z')) \
+ ? (c)-('a'-'A') : (c))
+#define tolowerif(c, u) ((((u) != 0) && ((c) >= 'A') && ((c) <= 'Z')) \
+ ? (c)-('A'-'a') : (c))
+
+static inline void
+io2vol(struct ncp_server *server, char *name, int case_trans)
+{
+ unsigned char nc;
+ unsigned char *np;
+ unsigned char *up;
+ struct nls_unicode uc;
+ struct nls_table *nls_in;
+ struct nls_table *nls_out;
+
+ nls_in = server->nls_io;
+ nls_out = server->nls_vol;
+ np = name;
+
+ while (*np)
+ {
+ nc = 0;
+ uc = nls_in->charset2uni[toupperif(*np, case_trans)];
+ up = nls_out->page_uni2charset[uc.uni2];
+ if (up != NULL) nc = up[uc.uni1];
+ if (nc != 0) *np = nc;
+ np++;
+ }
+}
+
+static inline void
+vol2io(struct ncp_server *server, char *name, int case_trans)
+{
+ unsigned char nc;
+ unsigned char *np;
+ unsigned char *up;
+ struct nls_unicode uc;
+ struct nls_table *nls_in;
+ struct nls_table *nls_out;
+
+ nls_in = server->nls_vol;
+ nls_out = server->nls_io;
+ np = name;
+
+ while (*np)
+ {
+ nc = 0;
+ uc = nls_in->charset2uni[*np];
+ up = nls_out->page_uni2charset[uc.uni2];
+ if (up != NULL) nc = up[uc.uni1];
+ if (nc == 0) nc = *np;
+ *np = tolowerif(nc, case_trans);
+ np++;
+ }
+}
+
+#else
+
+#define io2vol(S,N,U) if (U) str_upper(N)
+#define vol2io(S,N,U) if (U) str_lower(N)
+
+#endif /* CONFIG_NCPFS_NLS */
+
#endif /* _NCPLIB_H */
+
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
index cdcfc4220..865fc68a3 100644
--- a/fs/ncpfs/sock.c
+++ b/fs/ncpfs/sock.c
@@ -94,7 +94,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
poll_table wait_table;
struct poll_table_entry entry;
int init_timeout, max_timeout;
- int timeout; long tmp_timeout;
+ int timeout;
int retrans;
int major_timeout_seen;
int acknowledge_seen;
diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c
new file mode 100644
index 000000000..a923c218c
--- /dev/null
+++ b/fs/ncpfs/symlink.c
@@ -0,0 +1,212 @@
+/*
+ * linux/fs/ncpfs/symlink.c
+ *
+ * Code for allowing symbolic links on NCPFS (i.e. NetWare)
+ * Symbolic links are not supported on native NetWare, so we use an
+ * infrequently-used flag (Sh) and store a two-word magic header in
+ * the file to make sure we don't accidentally use a non-link file
+ * as a link.
+ *
+ * from linux/fs/ext2/symlink.c
+ *
+ * Copyright (C) 1998-99, Frank A. Vorstenbosch
+ *
+ * ncpfs symlink handling code
+ * NLS support (c) 1999 Petr Vandrovec
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_NCPFS_EXTRAS
+
+#include <asm/uaccess.h>
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ncp_fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#include "ncplib_kernel.h"
+
+
+/* these magic numbers must appear in the symlink file -- this makes it a bit
+ more resilient against the magic attributes being set on random files. */
+
+#define NCP_SYMLINK_MAGIC0 le32_to_cpu(0x6c6d7973) /* "symlnk->" */
+#define NCP_SYMLINK_MAGIC1 le32_to_cpu(0x3e2d6b6e)
+
+static int ncp_readlink(struct dentry *, char *, int);
+static struct dentry *ncp_follow_link(struct dentry *, struct dentry *, unsigned int);
+int ncp_create_new(struct inode *dir, struct dentry *dentry,
+ int mode,int attributes);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations ncp_symlink_inode_operations={
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ ncp_readlink, /* readlink */
+ ncp_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+/* ----- follow a symbolic link ------------------------------------------ */
+
+static struct dentry *ncp_follow_link(struct dentry *dentry,
+ struct dentry *base,
+ unsigned int follow)
+{
+ struct inode *inode=dentry->d_inode;
+ int error, length, cnt;
+ char *link;
+
+#ifdef DEBUG
+ printk("ncp_follow_link(dentry=%p,base=%p,follow=%u)\n",dentry,base,follow);
+#endif
+
+ if(!S_ISLNK(inode->i_mode)) {
+ dput(base);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if(ncp_make_open(inode,O_RDONLY)) {
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+
+ for (cnt = 0; (link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE+1, GFP_NFS))==NULL; cnt++) {
+ if (cnt > 10) {
+ dput(base);
+ return ERR_PTR(-EAGAIN); /* -ENOMEM? */
+ }
+ schedule();
+ }
+
+ error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle,
+ 0,NCP_MAX_SYMLINK_SIZE,link,&length);
+
+ if (error!=0 || length<NCP_MIN_SYMLINK_SIZE ||
+ ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 || ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) {
+ dput(base);
+ kfree(link);
+ return ERR_PTR(-EIO);
+ }
+
+ link[length]=0;
+
+ vol2io(NCP_SERVER(inode), link+8, 0);
+
+ /* UPDATE_ATIME(inode); */
+ base=lookup_dentry(link+8, base, follow);
+ kfree(link);
+
+ return base;
+}
+
+/* ----- read symbolic link ---------------------------------------------- */
+
+static int ncp_readlink(struct dentry * dentry, char * buffer, int buflen)
+{
+ struct inode *inode=dentry->d_inode;
+ char *link;
+ int length,error;
+
+#ifdef DEBUG
+ printk("ncp_readlink(dentry=%p,buffer=%p,buflen=%d)\n",dentry,buffer,buflen);
+#endif
+
+ if(!S_ISLNK(inode->i_mode))
+ return -EINVAL;
+
+ if(ncp_make_open(inode,O_RDONLY))
+ return -EIO;
+
+ if((link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE+1,GFP_NFS))==NULL)
+ return -ENOMEM;
+
+ error = ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle,
+ 0,NCP_MAX_SYMLINK_SIZE,link,&length);
+
+ if (error!=0 || length < NCP_MIN_SYMLINK_SIZE || buflen < (length-8) ||
+ ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 ||((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1) {
+ error = -EIO;
+ goto out;
+ }
+
+ link[length] = 0;
+
+ vol2io(NCP_SERVER(inode), link+8, 0);
+
+ error = length - 8;
+ if(copy_to_user(buffer, link+8, error))
+ error = -EFAULT;
+
+out:;
+ kfree(link);
+ return error;
+}
+
+/* ----- create a new symbolic link -------------------------------------- */
+
+int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) {
+ int i,length;
+ struct inode *inode;
+ char *link;
+
+#ifdef DEBUG
+ printk("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname);
+#endif
+
+ if (!(NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS))
+ return -EPERM; /* EPERM is returned by VFS if symlink procedure does not exist */
+
+ if ((length=strlen(symname))>NCP_MAX_SYMLINK_SIZE)
+ return -EINVAL;
+
+ if ((link=(char *)kmalloc(length+9,GFP_NFS))==NULL)
+ return -ENOMEM;
+
+ if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN)) {
+ kfree(link);
+ return -EIO;
+ }
+
+ inode=dentry->d_inode;
+
+ ((__u32 *)link)[0]=NCP_SYMLINK_MAGIC0;
+ ((__u32 *)link)[1]=NCP_SYMLINK_MAGIC1;
+ memcpy(link+8, symname, length+1); /* including last zero for io2vol */
+
+ /* map to/from server charset, do not touch upper/lower case as
+ symlink can point out of ncp filesystem */
+ io2vol(NCP_SERVER(inode), link+8, 0);
+
+ if(ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ 0, length+8, link, &i) || i!=length+8) {
+ kfree(link);
+ return -EIO;
+ }
+
+ kfree(link);
+ return 0;
+}
+#endif
+
+/* ----- EOF ----- */