diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-13 16:29:25 +0000 |
commit | db7d4daea91e105e3859cf461d7e53b9b77454b2 (patch) | |
tree | 9bb65b95440af09e8aca63abe56970dd3360cc57 /fs/ncpfs | |
parent | 9c1c01ead627bdda9211c9abd5b758d6c687d8ac (diff) |
Merge with Linux 2.2.8.
Diffstat (limited to 'fs/ncpfs')
-rw-r--r-- | fs/ncpfs/Config.in | 5 | ||||
-rw-r--r-- | fs/ncpfs/Makefile | 2 | ||||
-rw-r--r-- | fs/ncpfs/dir.c | 397 | ||||
-rw-r--r-- | fs/ncpfs/inode.c | 165 | ||||
-rw-r--r-- | fs/ncpfs/ioctl.c | 65 | ||||
-rw-r--r-- | fs/ncpfs/mmap.c | 2 | ||||
-rw-r--r-- | fs/ncpfs/ncplib_kernel.c | 54 | ||||
-rw-r--r-- | fs/ncpfs/ncplib_kernel.h | 93 | ||||
-rw-r--r-- | fs/ncpfs/sock.c | 2 | ||||
-rw-r--r-- | fs/ncpfs/symlink.c | 212 |
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 ----- */ |