diff options
Diffstat (limited to 'fs')
65 files changed, 1249 insertions, 428 deletions
diff --git a/fs/Config.in b/fs/Config.in index e48f75496..183aab8f8 100644 --- a/fs/Config.in +++ b/fs/Config.in @@ -99,7 +99,9 @@ if [ "$CONFIG_NET" = "y" ]; then fi dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET - + if [ "$CONFIG_SMB_FS" != "n" ]; then + string 'Default Remote NLS Option' CONFIG_SMB_NLS_REMOTE "" + fi if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then tristate 'NCP file system support (to mount NetWare volumes)' CONFIG_NCP_FS source fs/ncpfs/Config.in diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index 21a277344..b650f421f 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -40,12 +40,12 @@ adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) switch ((unsigned long)filp->f_pos) { case 0: - if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) goto free_out; filp->f_pos += 1; case 1: - if (filldir(dirent, "..", 2, 1, dir.parent_id) < 0) + if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0) goto free_out; filp->f_pos += 1; @@ -60,7 +60,7 @@ adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) goto unlock_out; while (ops->getnext(&dir, &obj) == 0) { if (filldir(dirent, obj.name, obj.name_len, - filp->f_pos, obj.file_id) < 0) + filp->f_pos, obj.file_id, DT_UNKNOWN) < 0) goto unlock_out; filp->f_pos += 1; } diff --git a/fs/affs/dir.c b/fs/affs/dir.c index aba6b0baa..c21e5771d 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -72,14 +72,14 @@ affs_readdir(struct file *filp, void *dirent, filldir_t filldir) if (filp->f_pos == 0) { filp->private_data = (void *)0; - if (filldir(dirent,".",1,filp->f_pos,inode->i_ino) < 0) { + if (filldir(dirent,".",1,filp->f_pos,inode->i_ino,DT_DIR) < 0) { return 0; } ++filp->f_pos; stored++; } if (filp->f_pos == 1) { - if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) { + if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode),DT_DIR) < 0) { return stored; } filp->f_pos = 2; @@ -135,7 +135,7 @@ affs_readdir(struct file *filp, void *dirent, filldir_t filldir) pr_debug("AFFS: readdir(): filldir(\"%.*s\",ino=%lu), i=%d\n", namelen,name,ino,i); filp->private_data = (void *)ino; - if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0) + if (filldir(dirent,name,namelen,filp->f_pos,ino,DT_UNKNOWN) < 0) goto readdir_done; filp->private_data = (void *)(unsigned long)i; affs_brelse(fh_bh); diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 4ba567499..0df477648 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -54,19 +54,19 @@ static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldi switch(nr) { case 0: - if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0) + if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0) return 0; filp->f_pos = ++nr; /* fall through */ case 1: - if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0) + if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0) return 0; filp->f_pos = ++nr; /* fall through */ default: while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr,ent) ) { if ( !ent->dentry || d_mountpoint(ent->dentry) ) { - if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0) + if (filldir(dirent,ent->name,ent->len,onr,ent->ino,DT_UNKNOWN) < 0) return 0; filp->f_pos = nr; } diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index aca401b73..9d21624bc 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -23,7 +23,9 @@ static int is_tree_busy(struct vfsmount *mnt) int count; spin_lock(&dcache_lock); - count = atomic_read(&mnt->mnt_count); + count = atomic_read(&mnt->mnt_count) - 2; + if (!is_autofs4_dentry(mnt->mnt_mountpoint)) + count--; repeat: next = this_parent->mnt_mounts.next; resume: @@ -33,7 +35,7 @@ resume: mnt_child); next = tmp->next; /* Decrement count for unused children */ - count += atomic_read(&p->mnt_count) - 1; + count += atomic_read(&p->mnt_count) - 2; if (!list_empty(&p->mnt_mounts)) { this_parent = p; goto repeat; diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 76e55be5d..721739e31 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -50,7 +50,7 @@ static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir) de = (struct bfs_dirent *)(bh->b_data + offset); if (de->ino) { int size = strnlen(de->name, BFS_NAMELEN); - if (filldir(dirent, de->name, size, f->f_pos, de->ino) < 0) { + if (filldir(dirent, de->name, size, f->f_pos, de->ino, DT_UNKNOWN) < 0) { brelse(bh); return 0; } diff --git a/fs/block_dev.c b/fs/block_dev.c index 7546705fa..21bc086e3 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -5,6 +5,7 @@ */ #include <linux/config.h> +#include <linux/init.h> #include <linux/mm.h> #include <linux/locks.h> #include <linux/fcntl.h> @@ -347,7 +348,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) } } -void bdev_init(void) +void __init bdev_init(void) { int i; struct list_head *head = bdev_hashtable; @@ -364,7 +365,7 @@ void bdev_init(void) 0, SLAB_HWCACHE_ALIGN, init_once, NULL); if (!bdev_cachep) - panic("cannot create bdev slab cache"); + panic("Cannot create bdev_cache SLAB cache"); } /* diff --git a/fs/buffer.c b/fs/buffer.c index b296dad7b..ca6e2f919 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -119,7 +119,7 @@ union bdflush_param { when trying to refill buffers. */ int interval; /* jiffies delay between kupdate flushes */ int age_buffer; /* Time for normal buffer to age before we flush it */ - int age_super; /* Time for superblock to age before we flush it */ + int dummy1; /* unused, was age_super */ int dummy2; /* unused */ int dummy3; /* unused */ } b_un; @@ -894,7 +894,7 @@ void balance_dirty(kdev_t dev) static __inline__ void __mark_dirty(struct buffer_head *bh, int flag) { - bh->b_flushtime = jiffies + (flag ? bdf_prm.b_un.age_super : bdf_prm.b_un.age_buffer); + bh->b_flushtime = jiffies + bdf_prm.b_un.age_buffer; refile_buffer(bh); } @@ -1714,8 +1714,10 @@ int generic_commit_write(struct file *file, struct page *page, loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to; __block_commit_write(inode,page,from,to); kunmap(page); - if (pos > inode->i_size) + if (pos > inode->i_size) { inode->i_size = pos; + mark_inode_dirty(inode); + } return 0; } @@ -1781,15 +1783,12 @@ static void end_buffer_io_kiobuf(struct buffer_head *bh, int uptodate) * for them to complete. Clean up the buffer_heads afterwards. */ -static int do_kio(int rw, int nr, struct buffer_head *bh[], int size) +static int wait_kio(int rw, int nr, struct buffer_head *bh[], int size) { int iosize; int i; struct buffer_head *tmp; - if (rw == WRITE) - rw = WRITERAW; - ll_rw_block(rw, nr, bh); iosize = 0; spin_lock(&unused_list_lock); @@ -1840,6 +1839,7 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], int pageind; int bhind; int offset; + int sectors = size>>9; unsigned long blocknr; struct kiobuf * iobuf = NULL; struct page * map; @@ -1891,9 +1891,10 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], tmp->b_this_page = tmp; init_buffer(tmp, end_buffer_io_kiobuf, iobuf); - tmp->b_dev = dev; + tmp->b_rdev = tmp->b_dev = dev; tmp->b_blocknr = blocknr; - tmp->b_state = 1 << BH_Mapped; + tmp->b_rsector = blocknr*sectors; + tmp->b_state = (1 << BH_Mapped) | (1 << BH_Lock) | (1 << BH_Req); if (rw == WRITE) { set_bit(BH_Uptodate, &tmp->b_state); @@ -1905,12 +1906,13 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], offset += size; atomic_inc(&iobuf->io_count); - + + generic_make_request(rw, tmp); /* - * Start the IO if we have got too much + * Wait for IO if we have got too much */ if (bhind >= KIO_MAX_SECTORS) { - err = do_kio(rw, bhind, bh, size); + err = wait_kio(rw, bhind, bh, size); if (err >= 0) transferred += err; else @@ -1928,7 +1930,7 @@ int brw_kiovec(int rw, int nr, struct kiobuf *iovec[], /* Is there any IO still left to submit? */ if (bhind) { - err = do_kio(rw, bhind, bh, size); + err = wait_kio(rw, bhind, bh, size); if (err >= 0) transferred += err; else diff --git a/fs/coda/dir.c b/fs/coda/dir.c index e53933710..a51bfc647 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -748,7 +748,7 @@ static int coda_venus_readdir(struct file *filp, void *getdent, char *name = vdirent->d_name; errfill = filldir(getdent, name, namlen, - offs, ino); + offs, ino, DT_UNKNOWN); CDEBUG(D_FILE, "entry %d: ino %ld, namlen %d, reclen %d, type %d, pos %d, string_offs %d, name %*s, offset %d, result: %d, errfill: %d.\n", i,vdirent->d_fileno, vdirent->d_namlen, vdirent->d_reclen, vdirent->d_type, pos, string_offset, vdirent->d_namlen, vdirent->d_name, (u_int) offs, result, errfill); /* errfill means no space for filling in this round */ if ( errfill < 0 ) { diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 8b0a91426..a0cf6ba97 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -258,7 +258,7 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir) break; namelen--; } - error = filldir(dirent, name, namelen, offset, CRAMINO(de)); + error = filldir(dirent, name, namelen, offset, CRAMINO(de), de->mode >> 12); if (error) break; diff --git a/fs/dcache.c b/fs/dcache.c index e2bcbe6a3..214f0da2e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1189,7 +1189,9 @@ static void __init dcache_init(unsigned long mempages) if (!dentry_cache) panic("Cannot create dentry cache"); +#if PAGE_SHIFT < 13 mempages >>= (13 - PAGE_SHIFT); +#endif mempages *= sizeof(struct list_head); for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++) ; @@ -1228,9 +1230,6 @@ static void __init dcache_init(unsigned long mempages) /* SLAB cache for __getname() consumers */ kmem_cache_t *names_cachep; -/* SLAB cache for files_struct structures */ -kmem_cache_t *files_cachep; - /* SLAB cache for file structures */ kmem_cache_t *filp_cachep; @@ -1246,7 +1245,7 @@ void __init vfs_caches_init(unsigned long mempages) sizeof(struct buffer_head), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); if(!bh_cachep) - panic("Cannot create buffer head SLAB cache\n"); + panic("Cannot create buffer head SLAB cache"); names_cachep = kmem_cache_create("names_cache", PAGE_SIZE, 0, @@ -1254,12 +1253,6 @@ void __init vfs_caches_init(unsigned long mempages) if (!names_cachep) panic("Cannot create names SLAB cache"); - files_cachep = kmem_cache_create("files_cache", - sizeof(struct files_struct), 0, - SLAB_HWCACHE_ALIGN, NULL, NULL); - if (!files_cachep) - panic("Cannot create files SLAB cache"); - filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); diff --git a/fs/devfs/base.c b/fs/devfs/base.c index ad28db63a..3ffb8275e 100644 --- a/fs/devfs/base.c +++ b/fs/devfs/base.c @@ -2439,14 +2439,14 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) case 0: scan_dir_for_removable (parent); err = (*filldir) (dirent, "..", 2, file->f_pos, - file->f_dentry->d_parent->d_inode->i_ino); + file->f_dentry->d_parent->d_inode->i_ino, DT_DIR); if (err == -EINVAL) break; if (err < 0) return err; file->f_pos++; ++stored; /* Fall through */ case 1: - err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino); + err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino, DT_DIR); if (err == -EINVAL) break; if (err < 0) return err; file->f_pos++; @@ -2463,7 +2463,7 @@ static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir) { if ( IS_HIDDEN (de) ) continue; err = (*filldir) (dirent, de->name, de->namelen, - file->f_pos, de->inode.ino); + file->f_pos, de->inode.ino, de->mode >> 12); if (err == -EINVAL) break; if (err < 0) return err; file->f_pos++; diff --git a/fs/devpts/root.c b/fs/devpts/root.c index 06f6b3baa..dd696709f 100644 --- a/fs/devpts/root.c +++ b/fs/devpts/root.c @@ -52,12 +52,12 @@ static int devpts_root_readdir(struct file *filp, void *dirent, filldir_t filldi switch(nr) { case 0: - if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0) + if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0) return 0; filp->f_pos = ++nr; /* fall through */ case 1: - if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0) + if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0) return 0; filp->f_pos = ++nr; /* fall through */ @@ -66,7 +66,7 @@ static int devpts_root_readdir(struct file *filp, void *dirent, filldir_t filldi int ptynr = nr - 2; if ( sbi->inodes[ptynr] ) { genptsname(numbuf, ptynr); - if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr) < 0 ) + if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr, DT_CHR) < 0 ) return 0; } filp->f_pos = ++nr; diff --git a/fs/efs/dir.c b/fs/efs/dir.c index 2829a60eb..ac64b8aeb 100644 --- a/fs/efs/dir.c +++ b/fs/efs/dir.c @@ -78,7 +78,7 @@ static int efs_readdir(struct file *filp, void *dirent, filldir_t filldir) { filp->f_pos = (block << EFS_DIRBSIZE_BITS) | slot; /* copy filename and data in dirslot */ - filldir(dirent, nameptr, namelen, filp->f_pos, inodenum); + filldir(dirent, nameptr, namelen, filp->f_pos, inodenum, DT_UNKNOWN); /* sanity check */ if (nameptr - (char *) dirblock + namelen > EFS_DIRBSIZE) { @@ -402,7 +402,10 @@ static int exec_mmap(void) if (mm) { struct mm_struct *active_mm = current->active_mm; - init_new_context(current, mm); + if (init_new_context(current, mm)) { + mmdrop(mm); + return -ENOMEM; + } task_lock(current); current->mm = mm; current->active_mm = mm; @@ -433,7 +436,7 @@ static inline int make_private_signals(void) if (atomic_read(¤t->sig->count) <= 1) return 0; - newsig = kmalloc(sizeof(*newsig), GFP_KERNEL); + newsig = kmem_cache_alloc(sigact_cachep, GFP_KERNEL); if (newsig == NULL) return -ENOMEM; spin_lock_init(&newsig->siglock); @@ -457,7 +460,7 @@ static inline void release_old_signals(struct signal_struct * oldsig) if (current->sig == oldsig) return; if (atomic_dec_and_test(&oldsig->count)) - kfree(oldsig); + kmem_cache_free(sigact_cachep, oldsig); } /* @@ -467,22 +470,30 @@ static inline void release_old_signals(struct signal_struct * oldsig) static inline void flush_old_files(struct files_struct * files) { - unsigned long j; + long j = -1; - j = 0; + write_lock(&files->file_lock); for (;;) { unsigned long set, i; + j++; i = j * __NFDBITS; if (i >= files->max_fds || i >= files->max_fdset) break; - set = xchg(&files->close_on_exec->fds_bits[j], 0); - j++; + set = files->close_on_exec->fds_bits[j]; + if (!set) + continue; + files->close_on_exec->fds_bits[j] = 0; + write_unlock(&files->file_lock); for ( ; set ; i++,set >>= 1) { - if (set & 1) + if (set & 1) { sys_close(i); + } } + write_lock(&files->file_lock); + } + write_unlock(&files->file_lock); } int flush_old_exec(struct linux_binprm * bprm) diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 3a18b375c..cd49b5da6 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -20,6 +20,10 @@ #include <linux/fs.h> +static unsigned char ext2_filetype_table[] = { + DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK +}; + static int ext2_readdir(struct file *, void *, filldir_t); struct file_operations ext2_dir_operations = { @@ -152,10 +156,15 @@ revalidate: * during the copy operation. */ unsigned long version = filp->f_version; + unsigned char d_type = DT_UNKNOWN; + if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE) + && de->file_type < EXT2_FT_MAX) + d_type = ext2_filetype_table[de->file_type]; error = filldir(dirent, de->name, de->name_len, - filp->f_pos, le32_to_cpu(de->inode)); + filp->f_pos, le32_to_cpu(de->inode), + d_type); if (error) break; if (version != filp->f_version) diff --git a/fs/fat/dir.c b/fs/fat/dir.c index bbb895285..5f7a643d7 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -313,7 +313,7 @@ static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, /* Fake . and .. for the root directory. */ if (inode->i_ino == MSDOS_ROOT_INO) { while (cpos < 2) { - if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO) < 0) + if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0) return 0; cpos++; filp->f_pos++; @@ -466,7 +466,8 @@ ParseLong: if (!long_slots||shortnames) { if (both) bufname[i] = '\0'; - if (filldir(dirent, bufname, i, *furrfu, inum) < 0) + if (filldir(dirent, bufname, i, *furrfu, inum, + (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) goto FillFailed; } else { char longname[275]; @@ -478,7 +479,8 @@ ParseLong: memcpy(&longname[long_len+1], bufname, i); long_len += i; } - if (filldir(dirent, longname, long_len, *furrfu, inum) < 0) + if (filldir(dirent, longname, long_len, *furrfu, inum, + (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) goto FillFailed; } @@ -508,7 +510,8 @@ static int vfat_ioctl_fill( const char * name, int name_len, off_t offset, - ino_t ino) + ino_t ino, + unsigned int d_type) { struct dirent *d1 = (struct dirent *)buf; struct dirent *d2 = d1 + 1; diff --git a/fs/fcntl.c b/fs/fcntl.c index 35a5dbc7d..7a3bcefb8 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -129,7 +129,7 @@ out_putf: asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) { int err = -EBADF; - struct file * file; + struct file * file, *tofree; struct files_struct * files = current->files; write_lock(&files->file_lock); @@ -144,31 +144,40 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) get_file(file); /* We are now finished with oldfd */ err = expand_files(files, newfd); - if (err < 0) { - write_unlock(&files->file_lock); - fput(file); - goto out; - } + if (err < 0) + goto out_fput; /* To avoid races with open() and dup(), we will mark the fd as * in-use in the open-file bitmap throughout the entire dup2() * process. This is quite safe: do_close() uses the fd array * entry, not the bitmap, to decide what work needs to be * done. --sct */ + /* Doesn't work. open() might be there first. --AV */ + + /* Yes. It's a race. In user space. Nothing sane to do */ + err = -EBUSY; + tofree = files->fd[newfd]; + if (!tofree && FD_ISSET(newfd, files->open_fds)) + goto out_fput; + + files->fd[newfd] = file; FD_SET(newfd, files->open_fds); + FD_CLR(newfd, files->close_on_exec); write_unlock(&files->file_lock); - - do_close(files, newfd, 0); - write_lock(&files->file_lock); - allocate_fd(files, file, newfd); + if (tofree) + filp_close(tofree, files); err = newfd; - out: return err; out_unlock: write_unlock(&files->file_lock); goto out; + +out_fput: + write_unlock(&files->file_lock); + fput(file); + goto out; } asmlinkage long sys_dup(unsigned int fildes) @@ -209,16 +218,11 @@ static int setfl(int fd, struct file * filp, unsigned long arg) return 0; } -asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) -{ - struct file * filp; - long err = -EBADF; +static long do_fcntl(unsigned int fd, unsigned int cmd, + unsigned long arg, struct file * filp) +{ + long err = 0; - filp = fget(fd); - if (!filp) - goto out; - err = 0; - lock_kernel(); switch (cmd) { case F_DUPFD: err = -EINVAL; @@ -228,13 +232,10 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) } break; case F_GETFD: - err = FD_ISSET(fd, current->files->close_on_exec); + err = get_close_on_exec(fd); break; case F_SETFD: - if (arg&1) - FD_SET(fd, current->files->close_on_exec); - else - FD_CLR(fd, current->files->close_on_exec); + set_close_on_exec(fd, arg&1); break; case F_GETFL: err = filp->f_flags; @@ -287,11 +288,60 @@ asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) err = sock_fcntl (filp, cmd, arg); break; } + + return err; +} + +asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + long err = -EBADF; + + filp = fget(fd); + if (!filp) + goto out; + + lock_kernel(); + err = do_fcntl(fd, cmd, arg, filp); + unlock_kernel(); + + fput(filp); +out: + return err; +} + +#if BITS_PER_LONG == 32 +asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + long err; + + err = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + lock_kernel(); + switch (cmd) { + case F_GETLK64: + err = fcntl_getlk64(fd, (struct flock64 *) arg); + break; + case F_SETLK64: + err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg); + break; + case F_SETLKW64: + err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg); + break; + default: + err = do_fcntl(fd, cmd, arg, filp); + break; + } unlock_kernel(); fput(filp); out: return err; } +#endif /* Table to convert sigio signal codes into poll band bitmaps */ diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c index bca53e087..9dfb4ab82 100644 --- a/fs/hfs/dir_cap.c +++ b/fs/hfs/dir_cap.c @@ -190,7 +190,7 @@ static int cap_readdir(struct file * filp, if (filp->f_pos == 0) { /* Entry 0 is for "." */ - if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) { + if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, DT_DIR)) { return 0; } filp->f_pos = 1; @@ -207,7 +207,7 @@ static int cap_readdir(struct file * filp, } if (filldir(dirent, DOT_DOT->Name, - DOT_DOT_LEN, 1, ntohl(cnid))) { + DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) { return 0; } filp->f_pos = 2; @@ -234,7 +234,7 @@ static int cap_readdir(struct file * filp, len = hfs_namein(dir, tmp_name, &((struct hfs_cat_key *)brec.key)->CName); if (filldir(dirent, tmp_name, len, - filp->f_pos, ino)) { + filp->f_pos, ino, DT_UNKNOWN)) { hfs_cat_close(entry, &brec); return 0; } @@ -250,7 +250,8 @@ static int cap_readdir(struct file * filp, /* In root dir last-2 entry is for ".rootinfo" */ if (filldir(dirent, DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_CAP_FNDR)) { + ntohl(entry->cnid) | HFS_CAP_FNDR, + DT_UNKNOWN)) { return 0; } } @@ -262,7 +263,8 @@ static int cap_readdir(struct file * filp, /* In normal dirs last-1 entry is for ".finderinfo" */ if (filldir(dirent, DOT_FINDERINFO->Name, DOT_FINDERINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_CAP_FDIR)) { + ntohl(entry->cnid) | HFS_CAP_FDIR, + DT_UNKNOWN)) { return 0; } } @@ -274,7 +276,8 @@ static int cap_readdir(struct file * filp, /* In normal dirs last entry is for ".resource" */ if (filldir(dirent, DOT_RESOURCE->Name, DOT_RESOURCE_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_CAP_RDIR)) { + ntohl(entry->cnid) | HFS_CAP_RDIR, + DT_UNKNOWN)) { return 0; } } diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c index 3d613a8e3..8e39508fa 100644 --- a/fs/hfs/dir_dbl.c +++ b/fs/hfs/dir_dbl.c @@ -183,7 +183,8 @@ static int dbl_readdir(struct file * filp, if (filp->f_pos == 0) { /* Entry 0 is for "." */ - if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) { + if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, + DT_DIR)) { return 0; } filp->f_pos = 1; @@ -192,7 +193,7 @@ static int dbl_readdir(struct file * filp, if (filp->f_pos == 1) { /* Entry 1 is for ".." */ if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1, - hfs_get_hl(entry->key.ParID))) { + hfs_get_hl(entry->key.ParID), DT_DIR)) { return 0; } filp->f_pos = 2; @@ -229,7 +230,8 @@ static int dbl_readdir(struct file * filp, &((struct hfs_cat_key *)brec.key)->CName); } - if (filldir(dirent, tmp_name, len, filp->f_pos, ino)) { + if (filldir(dirent, tmp_name, len, filp->f_pos, ino, + DT_UNKNOWN)) { hfs_cat_close(entry, &brec); return 0; } @@ -243,7 +245,8 @@ static int dbl_readdir(struct file * filp, /* In root dir last entry is for "%RootInfo" */ if (filldir(dirent, PCNT_ROOTINFO->Name, PCNT_ROOTINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_DBL_HDR)) { + ntohl(entry->cnid) | HFS_DBL_HDR, + DT_UNKNOWN)) { return 0; } } diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c index 992a94b67..4d050e698 100644 --- a/fs/hfs/dir_nat.c +++ b/fs/hfs/dir_nat.c @@ -191,7 +191,8 @@ static int nat_readdir(struct file * filp, if (filp->f_pos == 0) { /* Entry 0 is for "." */ - if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) { + if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, + DT_DIR)) { return 0; } filp->f_pos = 1; @@ -208,7 +209,7 @@ static int nat_readdir(struct file * filp, } if (filldir(dirent, DOT_DOT->Name, - DOT_DOT_LEN, 1, ntohl(cnid))) { + DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) { return 0; } filp->f_pos = 2; @@ -235,7 +236,7 @@ static int nat_readdir(struct file * filp, len = hfs_namein(dir, tmp_name, &((struct hfs_cat_key *)brec.key)->CName); if (filldir(dirent, tmp_name, len, - filp->f_pos, ino)) { + filp->f_pos, ino, DT_UNKNOWN)) { hfs_cat_close(entry, &brec); return 0; } @@ -250,14 +251,16 @@ static int nat_readdir(struct file * filp, /* In normal dirs entry 2 is for ".AppleDouble" */ if (filldir(dirent, DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_NAT_HDIR)) { + ntohl(entry->cnid) | HFS_NAT_HDIR, + DT_UNKNOWN)) { return 0; } } else if (type == HFS_NAT_HDIR) { /* In .AppleDouble entry 2 is for ".Parent" */ if (filldir(dirent, DOT_PARENT->Name, DOT_PARENT_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_NAT_HDR)) { + ntohl(entry->cnid) | HFS_NAT_HDR, + DT_UNKNOWN)) { return 0; } } @@ -270,7 +273,8 @@ static int nat_readdir(struct file * filp, (type == HFS_NAT_HDIR)) { if (filldir(dirent, ROOTINFO->Name, ROOTINFO_LEN, filp->f_pos, - ntohl(entry->cnid) | HFS_NAT_HDR)) { + ntohl(entry->cnid) | HFS_NAT_HDR, + DT_UNKNOWN)) { return 0; } } diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c index 15db6e936..40e432163 100644 --- a/fs/hpfs/dir.c +++ b/fs/hpfs/dir.c @@ -108,14 +108,14 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) return 0; } if (filp->f_pos == 0) { - if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) { + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) { hpfs_unlock_inode(inode); return 0; } filp->f_pos = 11; } if (filp->f_pos == 11) { - if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) { + if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir, DT_DIR) < 0) { hpfs_unlock_inode(inode); return 0; } @@ -144,7 +144,7 @@ int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) goto again; } tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3); - if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode) < 0) { + if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode, DT_UNKNOWN) < 0) { filp->f_pos = old_pos; if (tempname != (char *)de->name) kfree(tempname); hpfs_brelse4(&qbh); diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h index f13f2fa7e..4d342db5f 100644 --- a/fs/hpfs/hpfs.h +++ b/fs/hpfs/hpfs.h @@ -187,7 +187,9 @@ struct code_page_directory in data block */ secno code_page_data; /* sector number of a code_page_data containing c.p. array */ - unsigned index; /* index in c.p. array in that sector*/ + unsigned short index; /* index in c.p. array in that sector*/ + unsigned short unknown; /* some unknown value; usually 0; + 2 in Japanese version */ } array[31]; /* unknown length */ }; @@ -207,7 +209,7 @@ struct code_page_data struct { unsigned short ix; /* index */ unsigned short code_page_number; /* code page number */ - unsigned short zero1; + unsigned short unknown; /* the same as in cp directory */ unsigned char map[128]; /* upcase table for chars 80..ff */ unsigned short zero2; } code_page[3]; diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c index a0e0feada..290844b4d 100644 --- a/fs/hpfs/map.c +++ b/fs/hpfs/map.c @@ -59,6 +59,11 @@ char *hpfs_load_code_page(struct super_block *s, secno cps) cpds = cp->array[0].code_page_data; cpi = cp->array[0].index; brelse(bh); + + if (cpi >= 3) { + printk("HPFS: Code page index out of array\n"); + return NULL; + } if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL; if ((unsigned)cpd->offs[cpi] > 0x178) { diff --git a/fs/inode.c b/fs/inode.c index 455c4af6e..2199d6888 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -771,6 +771,7 @@ void iput(struct inode *inode) list_del(&inode->i_list); INIT_LIST_HEAD(&inode->i_list); inode->i_state|=I_FREEING; + inodes_stat.nr_inodes--; spin_unlock(&inode_lock); if (inode->i_data.nrpages) @@ -799,11 +800,11 @@ void iput(struct inode *inode) list_del(&inode->i_list); INIT_LIST_HEAD(&inode->i_list); inode->i_state|=I_FREEING; + inodes_stat.nr_inodes--; spin_unlock(&inode_lock); clear_inode(inode); } } - inodes_stat.nr_inodes--; destroy_inode(inode); } } diff --git a/fs/ioctl.c b/fs/ioctl.c index f02d766bd..21af77a3a 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -58,11 +58,11 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) lock_kernel(); switch (cmd) { case FIOCLEX: - FD_SET(fd, current->files->close_on_exec); + set_close_on_exec(fd, 1); break; case FIONCLEX: - FD_CLR(fd, current->files->close_on_exec); + set_close_on_exec(fd, 0); break; case FIONBIO: diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 1b99acb19..41b201767 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -202,7 +202,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, /* Handle the case of the '.' directory */ if (de->name_len[0] == 1 && de->name[0] == 0) { - if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) break; filp->f_pos += de_len; continue; @@ -213,7 +213,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, /* Handle the case of the '..' directory */ if (de->name_len[0] == 1 && de->name[0] == 1) { inode_number = filp->f_dentry->d_parent->d_inode->i_ino; - if (filldir(dirent, "..", 2, filp->f_pos, inode_number) < 0) + if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0) break; filp->f_pos += de_len; continue; @@ -258,7 +258,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp, } } if (len > 0) { - if (filldir(dirent, p, len, filp->f_pos, inode_number) < 0) + if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0) break; } filp->f_pos += de_len; diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 9ea2223d3..266e7bf64 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -10,7 +10,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: inode-v23.c,v 1.33 2000/08/09 15:59:06 dwmw2 Exp $ + * $Id: inode-v23.c,v 1.34 2000/08/10 08:58:00 dwmw2 Exp $ * * * Ported to Linux 2.3.x and MTD: @@ -44,7 +44,6 @@ #include <linux/fs.h> #include <linux/locks.h> #include <linux/smp_lock.h> -#include <linux/sched.h> #include <linux/ioctl.h> #include <linux/stat.h> #include <linux/blkdev.h> @@ -350,7 +349,7 @@ jffs_new_inode(const struct inode * dir, struct jffs_raw_inode *raw_inode, inode->i_mtime = raw_inode->mtime; inode->i_ctime = raw_inode->ctime; inode->i_blksize = PAGE_SIZE; - inode->i_blocks = (raw_inode->dsize + PAGE_SIZE - 1) >> PAGE_SHIFT; + inode->i_blocks = (inode->i_size + 511) >> 9; inode->i_version = 0; inode->i_flags = sb->s_flags; inode->u.generic_ip = (void *)jffs_find_file(c, raw_inode->ino); @@ -558,7 +557,7 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) D2(printk("jffs_readdir(): inode: 0x%p, filp: 0x%p\n", inode, filp)); if (filp->f_pos == 0) { D3(printk("jffs_readdir(): \".\" %lu\n", inode->i_ino)); - if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) { + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) { return 0; } filp->f_pos = 1; @@ -572,7 +571,7 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) inode->u.generic_ip)->pino; } D3(printk("jffs_readdir(): \"..\" %u\n", ddino)); - if (filldir(dirent, "..", 2, filp->f_pos, ddino) < 0) + if (filldir(dirent, "..", 2, filp->f_pos, ddino, DT_DIR) < 0) return 0; filp->f_pos++; } @@ -584,7 +583,7 @@ jffs_readdir(struct file *filp, void *dirent, filldir_t filldir) D3(printk("jffs_readdir(): \"%s\" ino: %u\n", (f->name ? f->name : ""), f->ino)); if (filldir(dirent, f->name, f->nsize, - filp->f_pos , f->ino) < 0) + filp->f_pos , f->ino, DT_UNKNOWN) < 0) return 0; filp->f_pos++; } @@ -1571,7 +1570,7 @@ jffs_read_inode(struct inode *inode) inode->i_mtime = f->mtime; inode->i_ctime = f->ctime; inode->i_blksize = PAGE_SIZE; - inode->i_blocks = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + inode->i_blocks = (inode->i_size + 511) >> 9; if (S_ISREG(inode->i_mode)) { inode->i_op = &jffs_file_inode_operations; inode->i_fop = &jffs_file_operations; diff --git a/fs/locks.c b/fs/locks.c index c1054c341..ae4cc8069 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -103,6 +103,11 @@ * * Fixed /proc/locks interface so that we can't overrun the buffer we are handed. * Andy Walker (andy@lysaker.kvaerner.no), May 12, 1997. + * + * Use slab allocator instead of kmalloc/kfree. + * Use generic list implementation from <linux/list.h>. + * Sped up posix_locks_deadlock by only considering blocked locks. + * Matthew Wilcox <willy@thepuffingroup.com>, March, 2000. */ #include <linux/malloc.h> @@ -205,8 +210,58 @@ static struct file_lock *flock_make_lock(struct file *filp, unsigned int type) /* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX * style lock. */ -static int posix_make_lock(struct file *filp, struct file_lock *fl, - struct flock *l) +static int flock_to_posix_lock(struct file *filp, struct file_lock *fl, + struct flock *l) +{ + loff_t start; + + switch (l->l_whence) { + case 0: /*SEEK_SET*/ + start = 0; + break; + case 1: /*SEEK_CUR*/ + start = filp->f_pos; + break; + case 2: /*SEEK_END*/ + start = filp->f_dentry->d_inode->i_size; + break; + default: + return (0); + } + + if (((start += l->l_start) < 0) || (l->l_len < 0)) + return (0); + fl->fl_end = start + l->l_len - 1; + if (l->l_len > 0 && fl->fl_end < 0) + return (0); + fl->fl_start = start; /* we record the absolute position */ + if (l->l_len == 0) + fl->fl_end = OFFSET_MAX; + + fl->fl_owner = current->files; + fl->fl_pid = current->pid; + fl->fl_file = filp; + fl->fl_flags = FL_POSIX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + + switch (l->l_type) { + case F_RDLCK: + case F_WRLCK: + case F_UNLCK: + fl->fl_type = l->l_type; + break; + default: + return (0); + } + + return (1); +} + +#if BITS_PER_LONG == 32 +static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl, + struct flock64 *l) { loff_t start; @@ -253,6 +308,7 @@ static int posix_make_lock(struct file *filp, struct file_lock *fl, return (1); } +#endif /* Check if two locks overlap each other. */ @@ -300,8 +356,8 @@ static void locks_insert_block(struct file_lock *blocker, locks_delete_block(waiter); } list_add_tail(&waiter->fl_block, &blocker->fl_block); -// list_add(&waiter->fl_link, &blocked_list); -// waiter->fl_next = blocker; + list_add(&waiter->fl_link, &blocked_list); + waiter->fl_next = blocker; } /* Wake up processes blocked waiting for blocker. @@ -460,34 +516,27 @@ static int posix_locks_deadlock(struct file_lock *caller_fl, struct file_lock *block_fl) { struct list_head *tmp; - void *caller_owner, *blocked_owner; + fl_owner_t caller_owner, blocked_owner; unsigned int caller_pid, blocked_pid; caller_owner = caller_fl->fl_owner; caller_pid = caller_fl->fl_pid; blocked_owner = block_fl->fl_owner; blocked_pid = block_fl->fl_pid; + tmp = blocked_list.next; next_task: if (caller_owner == blocked_owner && caller_pid == blocked_pid) return 1; - list_for_each(tmp, &file_lock_list) { - struct list_head *btmp; + while (tmp != &blocked_list) { struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link); - if (fl->fl_owner == NULL || list_empty(&fl->fl_block)) - continue; - list_for_each(btmp, &fl->fl_block) { - struct file_lock *bfl = list_entry(tmp, struct file_lock, fl_block); - if (bfl->fl_owner == blocked_owner && - bfl->fl_pid == blocked_pid) { - if (fl->fl_owner == caller_owner && - fl->fl_pid == caller_pid) { - return (1); - } - blocked_owner = fl->fl_owner; - blocked_pid = fl->fl_pid; - goto next_task; - } + tmp = tmp->next; + if ((fl->fl_owner == blocked_owner) + && (fl->fl_pid == blocked_pid)) { + fl = fl->fl_next; + blocked_owner = fl->fl_owner; + blocked_pid = fl->fl_pid; + goto next_task; } } return 0; @@ -816,13 +865,12 @@ int posix_lock_file(struct file *filp, struct file_lock *caller, if (right) { if (left == right) { /* The new lock breaks the old one in two pieces, - * so we have to use the second new lock (in this - * case, even F_UNLCK may fail!). + * so we have to use the second new lock. */ - locks_copy_lock(new_fl2, right); - locks_insert_lock(before, left); left = new_fl2; new_fl2 = NULL; + locks_copy_lock(left, right); + locks_insert_lock(before, left); } right->fl_start = caller->fl_end + 1; locks_wake_up_blocks(right, 0); @@ -909,7 +957,8 @@ int fcntl_getlk(unsigned int fd, struct flock *l) if (!filp) goto out; - if (!posix_make_lock(filp, file_lock, &flock)) + error = -EINVAL; + if (!flock_to_posix_lock(filp, file_lock, &flock)) goto out_putf; if (filp->f_op->lock) { @@ -928,6 +977,18 @@ int fcntl_getlk(unsigned int fd, struct flock *l) flock.l_type = F_UNLCK; if (fl != NULL) { flock.l_pid = fl->fl_pid; +#if BITS_PER_LONG == 32 + /* + * Make sure we can represent the posix lock via + * legacy 32bit flock. + */ + error = -EOVERFLOW; + if (fl->fl_start > OFFT_OFFSET_MAX) + goto out_putf; + if ((fl->fl_end != OFFSET_MAX) + && (fl->fl_end > OFFT_OFFSET_MAX)) + goto out_putf; +#endif flock.l_start = fl->fl_start; flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : fl->fl_end - fl->fl_start + 1; @@ -993,7 +1054,7 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) } error = -EINVAL; - if (!posix_make_lock(filp, file_lock, &flock)) + if (!flock_to_posix_lock(filp, file_lock, &flock)) goto out_putf; error = -EBADF; @@ -1044,6 +1105,151 @@ out: return error; } +#if BITS_PER_LONG == 32 +/* Report the first existing lock that would conflict with l. + * This implements the F_GETLK command of fcntl(). + */ +int fcntl_getlk64(unsigned int fd, struct flock64 *l) +{ + struct file *filp; + struct file_lock *fl, *file_lock = locks_alloc_lock(); + struct flock64 flock; + int error; + + error = -EFAULT; + if (copy_from_user(&flock, l, sizeof(flock))) + goto out; + error = -EINVAL; + if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) + goto out; + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + error = -EINVAL; + if (!flock64_to_posix_lock(filp, file_lock, &flock)) + goto out_putf; + + if (filp->f_op->lock) { + error = filp->f_op->lock(filp, F_GETLK, file_lock); + if (error < 0) + goto out_putf; + else if (error == LOCK_USE_CLNT) + /* Bypass for NFS with no locking - 2.0.36 compat */ + fl = posix_test_lock(filp, file_lock); + else + fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock); + } else { + fl = posix_test_lock(filp, file_lock); + } + + flock.l_type = F_UNLCK; + if (fl != NULL) { + flock.l_pid = fl->fl_pid; + flock.l_start = fl->fl_start; + flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : + fl->fl_end - fl->fl_start + 1; + flock.l_whence = 0; + flock.l_type = fl->fl_type; + } + error = -EFAULT; + if (!copy_to_user(l, &flock, sizeof(flock))) + error = 0; + +out_putf: + fput(filp); +out: + locks_free_lock(file_lock); + return error; +} + +/* Apply the lock described by l to an open file descriptor. + * This implements both the F_SETLK and F_SETLKW commands of fcntl(). + */ +int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l) +{ + struct file *filp; + struct file_lock *file_lock = locks_alloc_lock(); + struct flock64 flock; + struct inode *inode; + int error; + + /* + * This might block, so we do it before checking the inode. + */ + error = -EFAULT; + if (copy_from_user(&flock, l, sizeof(flock))) + goto out; + + /* Get arguments and validate them ... + */ + + error = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + + error = -EINVAL; + inode = filp->f_dentry->d_inode; + + /* Don't allow mandatory locks on files that may be memory mapped + * and shared. + */ + if (IS_MANDLOCK(inode) && + (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) { + struct vm_area_struct *vma; + struct address_space *mapping = inode->i_mapping; + spin_lock(&mapping->i_shared_lock); + for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) { + if (!(vma->vm_flags & VM_MAYSHARE)) + continue; + spin_unlock(&mapping->i_shared_lock); + error = -EAGAIN; + goto out_putf; + } + spin_unlock(&mapping->i_shared_lock); + } + + error = -EINVAL; + if (!flock64_to_posix_lock(filp, file_lock, &flock)) + goto out_putf; + + error = -EBADF; + switch (flock.l_type) { + case F_RDLCK: + if (!(filp->f_mode & FMODE_READ)) + goto out_putf; + break; + case F_WRLCK: + if (!(filp->f_mode & FMODE_WRITE)) + goto out_putf; + break; + case F_UNLCK: + break; + case F_SHLCK: + case F_EXLCK: + default: + error = -EINVAL; + goto out_putf; + } + + if (filp->f_op->lock != NULL) { + error = filp->f_op->lock(filp, cmd, file_lock); + if (error < 0) + goto out_putf; + } + error = posix_lock_file(filp, file_lock, cmd == F_SETLKW); + +out_putf: + fput(filp); +out: + locks_free_lock(file_lock); + return error; +} +#endif /* BITS_PER_LONG == 32 */ + /* * This function is called when the file is being removed * from the task's fd array. diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 6443f05ed..1cc918910 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -43,7 +43,7 @@ static int minix_readdir(struct file * filp, de = (struct minix_dir_entry *) (offset + bh->b_data); if (de->inode) { int size = strnlen(de->name, info->s_namelen); - if (filldir(dirent, de->name, size, filp->f_pos, de->inode) < 0) { + if (filldir(dirent, de->name, size, filp->f_pos, de->inode, DT_UNKNOWN) < 0) { brelse(bh); return 0; } diff --git a/fs/namei.c b/fs/namei.c index 97d9e2d22..75ffc6ef0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1690,7 +1690,8 @@ int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, triple_up(&old_dir->i_zombie, &new_dir->i_zombie, &target->i_zombie); - d_rehash(new_dentry); + if (d_unhashed(new_dentry)) + d_rehash(new_dentry); dput(new_dentry); } else double_up(&old_dir->i_zombie, diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 8cb5a5c06..1c0f13e3b 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -446,13 +446,13 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) result = 0; if (filp->f_pos == 0) { - if (filldir(dirent, ".", 1, 0, inode->i_ino)) + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR)) goto out; filp->f_pos = 1; } if (filp->f_pos == 1) { if (filldir(dirent, "..", 2, 1, - dentry->d_parent->d_inode->i_ino)) + dentry->d_parent->d_inode->i_ino, DT_DIR)) goto out; filp->f_pos = 2; } @@ -503,7 +503,7 @@ static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) goto invalid_cache; res = filldir(dirent, dent->d_name.name, dent->d_name.len, filp->f_pos, - dent->d_inode->i_ino); + dent->d_inode->i_ino, DT_UNKNOWN); dput(dent); if (res) goto finished; @@ -650,7 +650,7 @@ end_advance: if (!ino) ino = iunique(inode->i_sb, 2); ctl.filled = filldir(dirent, qname.name, qname.len, - filp->f_pos, ino); + filp->f_pos, ino, DT_UNKNOWN); if (!ctl.filled) filp->f_pos += 1; } diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 5c5c3a95f..11b8e5d79 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -264,7 +264,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, * retrieving the current dirent on the server */ fileid = nfs_fileid_to_ino_t(entry->ino); res = filldir(dirent, entry->name, entry->len, - entry->prev_cookie, fileid); + entry->prev_cookie, fileid, DT_UNKNOWN); if (res < 0) break; file->f_pos = desc->target = entry->cookie; @@ -890,9 +890,8 @@ static int nfs_safe_remove(struct dentry *dentry) struct inode *inode = dentry->d_inode; int error = -EBUSY, rehash = 0; - dfprintk(VFS, "NFS: safe_remove(%s/%s, %ld)\n", - dentry->d_parent->d_name.name, dentry->d_name.name, - inode->i_ino); + dfprintk(VFS, "NFS: safe_remove(%s/%s)\n", + dentry->d_parent->d_name.name, dentry->d_name.name); /* * Unhash the dentry while we remove the file ... @@ -910,7 +909,8 @@ static int nfs_safe_remove(struct dentry *dentry) goto out; } nfs_zap_caches(dir_i); - NFS_CACHEINV(inode); + if (inode) + NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); if (error < 0) goto out; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index db7c110e5..5e7ced09e 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -402,9 +402,6 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) server->rsize = nfs_block_size(fsinfo.rtpref, NULL); if (data->wsize == 0) server->wsize = nfs_block_size(fsinfo.wtpref, NULL); - server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); - if (server->dtsize > PAGE_CACHE_SIZE) - server->dtsize = PAGE_CACHE_SIZE; /* NFSv3: we don't have bsize, but rather rtmult and wtmult... */ if (!fsinfo.bsize) fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult; @@ -434,6 +431,12 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) server->wsize = server->wpages << PAGE_CACHE_SHIFT; } + server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); + if (server->dtsize > PAGE_CACHE_SIZE) + server->dtsize = PAGE_CACHE_SIZE; + if (server->dtsize > server->rsize) + server->dtsize = server->rsize; + maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN; if (server->namelen == 0 || server->namelen > maxlen) diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 373d7296c..574c26a15 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -321,12 +321,8 @@ nfs_proc_readdir(struct file *file, __u64 cookie, void *entry, struct nfs_readdirargs arg; struct nfs_readdirres res; struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, cred }; - struct nfs_server *server = NFS_DSERVER(dir); int status; - if (server->rsize < size) - size = server->rsize; - arg.fh = NFS_FH(dir); arg.cookie = cookie; arg.buffer = entry; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index cb97c40bc..a5bda60ba 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1291,7 +1291,7 @@ nfs_writeback_done(struct rpc_task *task) static void nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data) { - struct nfs_page *req; + struct nfs_page *first, *last; struct dentry *dentry; struct inode *inode; loff_t start, end, len; @@ -1299,32 +1299,28 @@ nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data) /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ - end = 0; - start = ~0; - req = nfs_list_entry(head->next); - dentry = req->wb_dentry; - data->dentry = dentry; - data->cred = req->wb_cred; + list_splice(head, &data->pages); + INIT_LIST_HEAD(head); + first = nfs_list_entry(data->pages.next); + last = nfs_list_entry(data->pages.prev); + dentry = first->wb_dentry; inode = dentry->d_inode; - while (!list_empty(head)) { - struct nfs_page *req; - loff_t rqstart, rqend; - req = nfs_list_entry(head->next); - nfs_list_remove_request(req); - nfs_list_add_request(req, &data->pages); - rqstart = page_offset(req->wb_page) + req->wb_offset; - rqend = rqstart + req->wb_bytes; - if (rqstart < start) - start = rqstart; - if (rqend > end) - end = rqend; - } - data->args.fh = NFS_FH(dentry); - data->args.offset = start; + + /* + * Determine the offset range of requests in the COMMIT call. + * We rely on the fact that data->pages is an ordered list... + */ + start = page_offset(first->wb_page) + first->wb_offset; + end = page_offset(last->wb_page) + (last->wb_offset + last->wb_bytes); len = end - start; /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */ - if (end >= inode->i_size || len > (~((u32)0) >> 1)) + if (end >= inode->i_size || len < 0 || len > (~((u32)0) >> 1)) len = 0; + + data->dentry = dentry; + data->cred = first->wb_cred; + data->args.fh = NFS_FH(dentry); + data->args.offset = start; data->res.count = data->args.count = (u32)len; data->res.fattr = &data->fattr; data->res.verf = &data->verf; diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 948566a6e..308db4cd5 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -669,7 +669,7 @@ nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p, #define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2)) static int encode_entry(struct readdir_cd *cd, const char *name, - int namlen, off_t offset, ino_t ino, int plus) + int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus) { u32 *p = cd->buffer; int buflen, slen, elen; @@ -747,16 +747,16 @@ noexec: int nfs3svc_encode_entry(struct readdir_cd *cd, const char *name, - int namlen, off_t offset, ino_t ino) + int namlen, off_t offset, ino_t ino, unsigned int d_type) { - return encode_entry(cd, name, namlen, offset, ino, 0); + return encode_entry(cd, name, namlen, offset, ino, d_type, 0); } int nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name, - int namlen, off_t offset, ino_t ino) + int namlen, off_t offset, ino_t ino, unsigned int d_type) { - return encode_entry(cd, name, namlen, offset, ino, 1); + return encode_entry(cd, name, namlen, offset, ino, d_type, 1); } /* FSSTAT */ diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 52a0348c1..d1b6306db 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -41,7 +41,7 @@ struct nfsd_getdents_callback { * the name matching the specified inode number. */ static int filldir_one(void * __buf, const char * name, int len, - off_t pos, ino_t ino) + off_t pos, ino_t ino, unsigned int d_type) { struct nfsd_getdents_callback *buf = __buf; struct qstr *qs = buf->name; diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 7b9546d93..9127e0869 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -390,7 +390,7 @@ nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p, int nfssvc_encode_entry(struct readdir_cd *cd, const char *name, - int namlen, off_t offset, ino_t ino) + int namlen, off_t offset, ino_t ino, unsigned int d_type) { u32 *p = cd->buffer; int buflen, slen; diff --git a/fs/nls/Config.in b/fs/nls/Config.in index c33e9d6b8..5c9754aca 100644 --- a/fs/nls/Config.in +++ b/fs/nls/Config.in @@ -4,7 +4,8 @@ # msdos and Joliet want NLS if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \ - -o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" ]; then + -o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" \ + -o "$CONFIG_SMB_FS" != "n" ]; then define_bool CONFIG_NLS y else define_bool CONFIG_NLS n diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c index 0d372ddfa..c99e81e6a 100644 --- a/fs/ntfs/fs.c +++ b/fs/ntfs/fs.c @@ -200,7 +200,7 @@ static int ntfs_printcb(ntfs_u8 *entry,void *param) /* filldir expects an off_t rather than an loff_t. Hope we don't have more than 65535 index records */ error=nf->filldir(nf->dirent,nf->name,nf->namelen, - (nf->ph<<16)|nf->pl,inum); + (nf->ph<<16)|nf->pl,inum,DT_UNKNOWN); ntfs_free(nf->name); /* Linux filldir errors are negative, other errors positive */ return error; @@ -226,11 +226,11 @@ static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir) if(cb.ph==0xFFFF){ /* FIXME: Maybe we can return those with the previous call */ switch(cb.pl){ - case 0: filldir(dirent,".",1,filp->f_pos,dir->i_ino); + case 0: filldir(dirent,".",1,filp->f_pos,dir->i_ino,DT_DIR); filp->f_pos=0xFFFF0001; return 0; /* FIXME: parent directory */ - case 1: filldir(dirent,"..",2,filp->f_pos,0); + case 1: filldir(dirent,"..",2,filp->f_pos,0,DT_DIR); filp->f_pos=0xFFFF0002; return 0; } @@ -680,7 +680,7 @@ int get_unused_fd(void) repeat: fd = find_next_zero_bit(files->open_fds, - current->files->max_fdset, + files->max_fdset, files->next_fd); /* @@ -691,7 +691,7 @@ repeat: goto out; /* Do we need to expand the fdset array? */ - if (fd >= current->files->max_fdset) { + if (fd >= files->max_fdset) { error = expand_fdset(files, fd); if (!error) { error = -EMFILE; @@ -799,36 +799,27 @@ int filp_close(struct file *filp, fl_owner_t id) * Careful here! We test whether the file pointer is NULL before * releasing the fd. This ensures that one clone task can't release * an fd while another clone is opening it. - * - * The "release" argument tells us whether or not to mark the fd as free - * or not in the open-files bitmap. dup2 uses this to retain the fd - * without races. */ -int do_close(struct files_struct *files, unsigned int fd, int release) +asmlinkage long sys_close(unsigned int fd) { - int error; struct file * filp; + struct files_struct *files = current->files; - error = -EBADF; write_lock(&files->file_lock); - filp = frip(files, fd); + if (fd >= files->max_fds) + goto out_unlock; + filp = files->fd[fd]; if (!filp) goto out_unlock; + files->fd[fd] = NULL; FD_CLR(fd, files->close_on_exec); - if (release) - __put_unused_fd(files, fd); + __put_unused_fd(files, fd); write_unlock(&files->file_lock); - error = filp_close(filp, files); -out: - return error; + return filp_close(filp, files); + out_unlock: write_unlock(&files->file_lock); - goto out; -} - -asmlinkage long sys_close(unsigned int fd) -{ - return do_close(current->files, fd, 1); + return -EBADF; } /* diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index abd6ec28e..e264dd3d3 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -1,4 +1,4 @@ -/* $Id: inode.c,v 1.12 2000/07/13 08:06:42 davem Exp $ +/* $Id: inode.c,v 1.13 2000/08/12 13:25:46 davem Exp $ * openpromfs.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) @@ -757,14 +757,14 @@ static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filld i = filp->f_pos; switch (i) { case 0: - if (filldir(dirent, ".", 1, i, ino) < 0) return 0; + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) return 0; i++; filp->f_pos++; /* fall thru */ case 1: if (filldir(dirent, "..", 2, i, (NODE(ino).parent == 0xffff) ? - OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent)) < 0) + OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent), DT_DIR) < 0) return 0; i++; filp->f_pos++; @@ -780,14 +780,14 @@ static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filld if (prom_getname (nodes[node].node, buffer, 128) < 0) return 0; if (filldir(dirent, buffer, strlen(buffer), - filp->f_pos, NODE2INO(node)) < 0) + filp->f_pos, NODE2INO(node), DT_DIR) < 0) return 0; filp->f_pos++; node = nodes[node].next; } j = NODEP2INO(NODE(ino).first_prop); if (!i) { - if (filldir(dirent, ".node", 5, filp->f_pos, j) < 0) + if (filldir(dirent, ".node", 5, filp->f_pos, j, DT_REG) < 0) return 0; filp->f_pos++; } else @@ -798,7 +798,7 @@ static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filld if (alias_names [i]) { if (filldir (dirent, alias_names [i], strlen (alias_names [i]), - filp->f_pos, j) < 0) return 0; + filp->f_pos, j, DT_REG) < 0) return 0; filp->f_pos++; } } @@ -810,7 +810,7 @@ static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filld if (i) i--; else { if (filldir(dirent, p, strlen(p), - filp->f_pos, j) < 0) + filp->f_pos, j, DT_REG) < 0) return 0; filp->f_pos++; } @@ -545,9 +545,9 @@ int do_pipe(int *fd) this.len = strlen(name); this.hash = inode->i_ino; /* will go */ dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this); - dentry->d_op = &pipefs_dentry_operations; if (!dentry) goto close_f12_inode_i_j; + dentry->d_op = &pipefs_dentry_operations; d_add(dentry, inode); f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt)); f1->f_dentry = f2->f_dentry = dget(dentry); diff --git a/fs/proc/array.c b/fs/proc/array.c index 9137fc765..4bcb92d31 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -301,12 +301,11 @@ int proc_pid_stat(struct task_struct *task, char * buffer) { unsigned long vsize, eip, esp, wchan; long priority, nice; - int tty_pgrp; + int tty_pgrp = -1, tty_nr = 0; sigset_t sigign, sigcatch; char state; int res; pid_t ppid; - int tty_nr; struct mm_struct *mm; state = *get_task_state(task); @@ -315,6 +314,10 @@ int proc_pid_stat(struct task_struct *task, char * buffer) mm = task->mm; if(mm) atomic_inc(&mm->mm_users); + if (task->tty) { + tty_pgrp = task->tty->pgrp; + tty_nr = kdev_t_to_nr(task->tty->device); + } task_unlock(task); if (mm) { struct vm_area_struct *vma; @@ -333,14 +336,6 @@ int proc_pid_stat(struct task_struct *task, char * buffer) collect_sigign_sigcatch(task, &sigign, &sigcatch); - task_lock(task); - if (task->tty) - tty_pgrp = task->tty->pgrp; - else - tty_pgrp = -1; - tty_nr = task->tty ? kdev_t_to_nr(task->tty->device) : 0; - task_unlock(task); - /* scale priority and nice values from timeslices to -20..20 */ /* to make it look like a "normal" Unix priority/nice value */ priority = task->counter; diff --git a/fs/proc/base.c b/fs/proc/base.c index 23151f3d9..7625e4d5a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -522,12 +522,12 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) fd = filp->f_pos; switch (fd) { case 0: - if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) goto out; filp->f_pos++; case 1: ino = fake_ino(pid, PROC_PID_INO); - if (filldir(dirent, "..", 2, 1, ino) < 0) + if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) goto out; filp->f_pos++; default: @@ -555,7 +555,7 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) } while (i); ino = fake_ino(pid, PROC_PID_FD_DIR + fd); - if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0) + if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) break; } put_files_struct(files); @@ -578,13 +578,13 @@ static int proc_base_readdir(struct file * filp, i = filp->f_pos; switch (i) { case 0: - if (filldir(dirent, ".", 1, i, inode->i_ino) < 0) + if (filldir(dirent, ".", 1, i, inode->i_ino, DT_DIR) < 0) return 0; i++; filp->f_pos++; /* fall through */ case 1: - if (filldir(dirent, "..", 2, i, PROC_ROOT_INO) < 0) + if (filldir(dirent, "..", 2, i, PROC_ROOT_INO, DT_DIR) < 0) return 0; i++; filp->f_pos++; @@ -595,7 +595,8 @@ static int proc_base_readdir(struct file * filp, return 1; p = base_stuff + i; while (p->name) { - if (filldir(dirent, p->name, p->len, filp->f_pos, fake_ino(pid, p->type)) < 0) + if (filldir(dirent, p->name, p->len, filp->f_pos, + fake_ino(pid, p->type), p->mode >> 12) < 0) return 0; filp->f_pos++; p++; @@ -1007,7 +1008,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) if (!nr) { ino_t ino = fake_ino(0,PROC_PID_INO); - if (filldir(dirent, "self", 4, filp->f_pos, ino) < 0) + if (filldir(dirent, "self", 4, filp->f_pos, ino, DT_LNK) < 0) return 0; filp->f_pos++; nr++; @@ -1022,7 +1023,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) do buf[--j] = '0' + (pid % 10); while (pid/=10); - if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino) < 0) + if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) break; filp->f_pos++; } diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 9c7270070..ba2f88e9f 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -294,15 +294,15 @@ int proc_readdir(struct file * filp, i = filp->f_pos; switch (i) { case 0: - if (filldir(dirent, ".", 1, i, ino) < 0) + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) return 0; i++; filp->f_pos++; /* fall through */ case 1: if (filldir(dirent, "..", 2, i, - filp->f_dentry->d_parent->d_inode->i_ino - ) < 0) + filp->f_dentry->d_parent->d_inode->i_ino, + DT_DIR) < 0) return 0; i++; filp->f_pos++; @@ -320,7 +320,8 @@ int proc_readdir(struct file * filp, } do { - if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino) < 0) + if (filldir(dirent, de->name, de->namelen, filp->f_pos, + de->low_ino, de->mode >> 12) < 0) return 0; filp->f_pos++; de = de->next; diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 4b146945a..ac5d09b91 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -62,7 +62,7 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir) QNX4_INODES_PER_BLOCK + le->dl_inode_ndx; } - if (filldir(dirent, de->di_fname, size, filp->f_pos, ino) < 0) { + if (filldir(dirent, de->di_fname, size, filp->f_pos, ino, DT_UNKNOWN) < 0) { brelse(bh); return 0; } diff --git a/fs/readdir.c b/fs/readdir.c index e1e90c113..cd8f7ad3d 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -13,9 +13,7 @@ #include <asm/uaccess.h> -int vfs_readdir(struct file *file, - int (*filler)(void *,const char *,int,off_t,ino_t), - void *buf) +int vfs_readdir(struct file *file, filldir_t filler, void *buf) { struct inode *inode = file->f_dentry->d_inode; int res = -ENOTDIR; @@ -49,13 +47,13 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) i = filp->f_pos; switch (i) { case 0: - if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino) < 0) + if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0) break; i++; filp->f_pos++; /* fallthrough */ case 1: - if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino) < 0) + if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) break; i++; filp->f_pos++; @@ -83,7 +81,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir) if (!list_empty(&de->d_hash) && de->d_inode) { spin_unlock(&dcache_lock); - if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino) < 0) + if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0) break; spin_lock(&dcache_lock); } @@ -124,7 +122,8 @@ struct readdir_callback { int count; }; -static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) +static int fillonedir(void * __buf, const char * name, int namlen, off_t offset, + ino_t ino, unsigned int d_type) { struct readdir_callback * buf = (struct readdir_callback *) __buf; struct old_linux_dirent * dirent; @@ -184,7 +183,8 @@ struct getdents_callback { int error; }; -static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino) +static int filldir(void * __buf, const char * name, int namlen, off_t offset, + ino_t ino, unsigned int d_type) { struct linux_dirent * dirent; struct getdents_callback * buf = (struct getdents_callback *) __buf; @@ -240,3 +240,89 @@ out_putf: out: return error; } + +/* + * And even better one including d_type field and 64bit d_ino and d_off. + */ +struct linux_dirent64 { + u64 d_ino; + s64 d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[0]; +}; + +#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) + +struct getdents_callback64 { + struct linux_dirent64 * current_dir; + struct linux_dirent64 * previous; + int count; + int error; +}; + +static int filldir64(void * __buf, const char * name, int namlen, off_t offset, + ino_t ino, unsigned int d_type) +{ + struct linux_dirent64 * dirent, d; + struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; + int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) { + d.d_off = offset; + copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off)); + } + dirent = buf->current_dir; + buf->previous = dirent; + memset(&d, 0, NAME_OFFSET(&d)); + d.d_ino = ino; + d.d_reclen = reclen; + d.d_type = d_type; + copy_to_user(dirent, &d, NAME_OFFSET(&d)); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count) +{ + struct file * file; + struct linux_dirent64 * lastdirent; + struct getdents_callback64 buf; + int error; + + error = -EBADF; + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = (struct linux_dirent64 *) dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, filldir64, &buf); + if (error < 0) + goto out_putf; + error = buf.error; + lastdirent = buf.previous; + if (lastdirent) { + struct linux_dirent64 d; + d.d_off = file->f_pos; + copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off)); + error = count - buf.count; + } + +out_putf: + fput(file); +out: + return error; +} + diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 8d01f17ba..081209a48 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -254,6 +254,10 @@ romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long return res; } +static unsigned char romfs_dtype_table[] = { + DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO +}; + static int romfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { @@ -298,7 +302,8 @@ romfs_readdir(struct file *filp, void *dirent, filldir_t filldir) nextfh = ntohl(ri.next); if ((nextfh & ROMFH_TYPE) == ROMFH_HRD) ino = ntohl(ri.spec); - if (filldir(dirent, fsname, j, offset, ino) < 0) { + if (filldir(dirent, fsname, j, offset, ino, + romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) { return stored; } stored++; diff --git a/fs/smbfs/ChangeLog b/fs/smbfs/ChangeLog index 893890334..a57b1cc67 100644 --- a/fs/smbfs/ChangeLog +++ b/fs/smbfs/ChangeLog @@ -1,5 +1,16 @@ ChangeLog for smbfs. +2000-08-14 Urban Widmark <urban@svenskatest.se> + + * dir.c: support case sensitive shares + * inode.c: ascii mount options + * proc.c: check length of paths to avoid buffer overflow + * proc.c: don't do interruptable_sleep in smb_retry to avoid signal + problem/race. + * proc.c: O_RDONLY & smb_revalidate_inode fix (tail -f) + * proc.c: add nls support + * sock.c: attempt to fix smb_data_callback (avoid infinite loop) + 2000-07-25 Urban Widmark <urban@svenskatest.se> * proc.c: fix 3 places where bad server responses could cause an Oops. diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile index 3ea59f6cc..825b0156a 100644 --- a/fs/smbfs/Makefile +++ b/fs/smbfs/Makefile @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := smbfs.o -O_OBJS := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o +O_OBJS := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o M_OBJS := $(O_TARGET) # If you want debugging output, you may add these flags to the EXTRA_CFLAGS diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c index 807d8f2e3..b2d5c4099 100644 --- a/fs/smbfs/dir.c +++ b/fs/smbfs/dir.c @@ -14,6 +14,7 @@ #include <linux/ctype.h> #include <linux/smb_fs.h> +#include <linux/smb_mount.h> #include <linux/smbno.h> #include "smb_debug.h" @@ -66,12 +67,12 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir) switch ((unsigned int) filp->f_pos) { case 0: - if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0) + if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) goto out; filp->f_pos = 1; case 1: if (filldir(dirent, "..", 2, 1, - dentry->d_parent->d_inode->i_ino) < 0) + dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) goto out; filp->f_pos = 2; } @@ -127,7 +128,7 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir) } if (filldir(dirent, entry->name, entry->len, - filp->f_pos, entry->ino) < 0) + filp->f_pos, entry->ino, DT_UNKNOWN) < 0) break; filp->f_pos += 1; } @@ -142,7 +143,7 @@ out: } /* - * Note: in order to allow the smbclient process to open the + * Note: in order to allow the smbmount process to open the * mount point, we don't revalidate if conn_pid is NULL. */ static int @@ -190,6 +191,13 @@ static struct dentry_operations smbfs_dentry_operations = d_delete: smb_delete_dentry, }; +static struct dentry_operations smbfs_dentry_operations_case = +{ + d_revalidate: smb_lookup_validate, + d_delete: smb_delete_dentry, +}; + + /* * This is the callback when the dcache has a lookup hit. */ @@ -249,8 +257,7 @@ smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b) if (a->len != b->len) goto out; - for (i=0; i < a->len; i++) - { + for (i=0; i < a->len; i++) { if (tolower(a->name[i]) != tolower(b->name[i])) goto out; } @@ -300,6 +307,7 @@ smb_lookup(struct inode *dir, struct dentry *dentry) struct smb_fattr finfo; struct inode *inode; int error; + struct smb_sb_info *server; error = -ENAMETOOLONG; if (dentry->d_name.len > SMB_MAXNAMELEN) @@ -315,15 +323,18 @@ smb_lookup(struct inode *dir, struct dentry *dentry) inode = NULL; if (error == -ENOENT) goto add_entry; - if (!error) - { + if (!error) { error = -EACCES; finfo.f_ino = smb_invent_inos(1); inode = smb_iget(dir->i_sb, &finfo); - if (inode) - { + if (inode) { add_entry: - dentry->d_op = &smbfs_dentry_operations; + server = server_from_dentry(dentry); + if (server->mnt->flags & SMB_MOUNT_CASE) + dentry->d_op = &smbfs_dentry_operations_case; + else + dentry->d_op = &smbfs_dentry_operations; + d_add(dentry, inode); smb_renew_times(dentry); error = 0; diff --git a/fs/smbfs/getopt.c b/fs/smbfs/getopt.c new file mode 100644 index 000000000..0c5d111b3 --- /dev/null +++ b/fs/smbfs/getopt.c @@ -0,0 +1,61 @@ +/* + * getopt.c + */ + +#include <linux/kernel.h> +#include <linux/string.h> + +#include "getopt.h" + +/** + * smb_getopt - option parser + * @caller: name of the caller, for error messages + * @options: the options string + * @opts: an array of &struct option entries controlling parser operations + * @optopt: output; will contain the current option + * @optarg: output; will contain the value (if one exists) + * @flag: output; may be NULL; should point to a long for or'ing flags + * @value: output; may be NULL; will be overwritten with the integer value + * of the current argument. + * + * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). + * Returns opts->val if a matching entry in the 'opts' array is found, + * 0 when no more tokens are found, -1 if an error is encountered. + */ +int smb_getopt(char *caller, char **options, struct option *opts, + char **optopt, char **optarg, unsigned long *flag, + unsigned long *value) +{ + char *token; + char *val; + int i; + + if ( (token = strsep(options, ",")) == NULL) + return 0; + *optopt = token; + + *optarg = NULL; + if ((val = strchr (token, '=')) != NULL) { + *val++ = 0; + if (value) + *value = simple_strtoul(val, NULL, 0); + *optarg = val; + } + + for (i = 0; opts[i].name != NULL; i++) { + if (!strcmp(opts[i].name, token)) { + if (opts[i].has_arg && (!val || !*val)) { + printk("%s: the %s option requires an argument\n", + caller, token); + return -1; + } + + if (flag && opts[i].flag) + *flag |= opts[i].flag; + + return opts[i].val; + } + } + printk("%s: Unrecognized mount option %s\n", caller, token); + return -1; +} diff --git a/fs/smbfs/getopt.h b/fs/smbfs/getopt.h new file mode 100644 index 000000000..691f21103 --- /dev/null +++ b/fs/smbfs/getopt.h @@ -0,0 +1,15 @@ +#ifndef _LINUX_GETOPT_H +#define _LINUX_GETOPT_H + +struct option { + const char *name; + int has_arg; + unsigned long flag; + int val; +}; + +extern int smb_getopt(char *caller, char **options, struct option *opts, + char **optopt, char **optarg, unsigned long *flag, + unsigned long *value); + +#endif /* _LINUX_GETOPT_H */ diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index d7cb992b8..708951949 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -7,6 +7,7 @@ * Please add a note about your changes to smbfs in the ChangeLog file. */ +#include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -20,6 +21,7 @@ #include <linux/file.h> #include <linux/dcache.h> #include <linux/smp_lock.h> +#include <linux/nls.h> #include <linux/smb_fs.h> #include <linux/smbno.h> @@ -29,6 +31,7 @@ #include <asm/uaccess.h> #include "smb_debug.h" +#include "getopt.h" static void smb_delete_inode(struct inode *); static void smb_put_super(struct super_block *); @@ -282,6 +285,82 @@ smb_delete_inode(struct inode *ino) clear_inode(ino); } +/* FIXME: flags and has_arg could probably be merged. */ +struct option opts[] = { + { "version", 1, 0, 'v' }, + { "win95", 0, SMB_MOUNT_WIN95, 1 }, + { "oldattr", 0, SMB_MOUNT_OLDATTR, 1 }, + { "dirattr", 0, SMB_MOUNT_DIRATTR, 1 }, + { "case", 0, SMB_MOUNT_CASE, 1 }, + { "uid", 1, 0, 'u' }, + { "gid", 1, 0, 'g' }, + { "file_mode", 1, 0, 'f' }, + { "dir_mode", 1, 0, 'd' }, + { "iocharset", 1, 0, 'i' }, + { "codepage", 1, 0, 'c' }, + { NULL, 0, 0, 0} +}; + +static int +parse_options(struct smb_mount_data_kernel *mnt, char *options) +{ + int c; + unsigned long flags; + unsigned long value; + char *optarg; + char *optopt; + + flags = 0; + while ( (c = smb_getopt("smbfs", &options, opts, + &optopt, &optarg, &flags, &value)) > 0) { + + VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : "<none>"); + + switch (c) { + case 1: + /* got a "flag" option */ + break; + case 'v': + if (value != SMB_MOUNT_VERSION) { + printk ("smbfs: Bad mount version %ld, expected %d\n", + value, SMB_MOUNT_VERSION); + return 0; + } + mnt->version = value; + break; + case 'u': + mnt->uid = value; + break; + case 'g': + mnt->gid = value; + break; + case 'f': + mnt->file_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO); + mnt->file_mode |= S_IFREG; + break; + case 'd': + mnt->dir_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO); + mnt->dir_mode |= S_IFDIR; + break; + case 'i': + strncpy(mnt->codepage.local_name, optarg, + SMB_NLS_MAXNAMELEN); + break; + case 'c': + strncpy(mnt->codepage.remote_name, optarg, + SMB_NLS_MAXNAMELEN); + break; + default: + printk ("smbfs: Unrecognized mount option %s\n", + optopt); + return -1; + } + } + mnt->flags = flags; + return c; +} + + static void smb_put_super(struct super_block *sb) { @@ -300,18 +379,32 @@ smb_put_super(struct super_block *sb) kfree(sb->u.smbfs_sb.temp_buf); if (server->packet) smb_vfree(server->packet); + + if(sb->u.smbfs_sb.remote_nls) { + unload_nls(sb->u.smbfs_sb.remote_nls); + sb->u.smbfs_sb.remote_nls = NULL; + } + if(sb->u.smbfs_sb.local_nls) { + unload_nls(sb->u.smbfs_sb.local_nls); + sb->u.smbfs_sb.local_nls = NULL; + } } struct super_block * smb_read_super(struct super_block *sb, void *raw_data, int silent) { - struct smb_mount_data *mnt; + struct smb_mount_data_kernel *mnt; + struct smb_mount_data *oldmnt; struct inode *root_inode; struct smb_fattr root; + int ver; if (!raw_data) goto out_no_data; - if (((struct smb_mount_data *) raw_data)->version != SMB_MOUNT_VERSION) + + oldmnt = (struct smb_mount_data *) raw_data; + ver = oldmnt->version; + if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII) goto out_wrong_data; sb->s_blocksize = 1024; /* Eh... Is this correct? */ @@ -320,6 +413,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) sb->s_flags = 0; sb->s_op = &smb_sops; + sb->u.smbfs_sb.mnt = NULL; sb->u.smbfs_sb.sock_file = NULL; init_MUTEX(&sb->u.smbfs_sb.sem); init_waitqueue_head(&sb->u.smbfs_sb.wait); @@ -332,30 +426,61 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) goto out_no_mem; /* Allocate the global temp buffer */ - sb->u.smbfs_sb.temp_buf = kmalloc(SMB_MAXPATHLEN + 20, GFP_KERNEL); + sb->u.smbfs_sb.temp_buf = kmalloc(2*SMB_MAXPATHLEN + 20, GFP_KERNEL); if (!sb->u.smbfs_sb.temp_buf) goto out_no_temp; + /* Setup NLS stuff */ + sb->u.smbfs_sb.remote_nls = NULL; + sb->u.smbfs_sb.local_nls = NULL; + sb->u.smbfs_sb.name_buf = sb->u.smbfs_sb.temp_buf + SMB_MAXPATHLEN + 20; + /* Allocate the mount data structure */ - mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL); + /* FIXME: merge this with the other malloc and get a whole page? */ + mnt = kmalloc(sizeof(struct smb_mount_data_kernel), GFP_KERNEL); if (!mnt) goto out_no_mount; - *mnt = *((struct smb_mount_data *) raw_data); - /* FIXME: passes config flags in high bits of file mode. Should be a - separate flags field. (but smbmount includes kernel headers ...) */ - mnt->version = (mnt->file_mode >> 9); - mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO); - mnt->file_mode |= S_IFREG; - mnt->dir_mode &= (S_IRWXU | S_IRWXG | S_IRWXO); - mnt->dir_mode |= S_IFDIR; sb->u.smbfs_sb.mnt = mnt; + + memset(mnt, 0, sizeof(struct smb_mount_data_kernel)); + strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT, + SMB_NLS_MAXNAMELEN); + strncpy(mnt->codepage.local_name, CONFIG_SMB_NLS_REMOTE, + SMB_NLS_MAXNAMELEN); + + if (ver == SMB_MOUNT_OLDVERSION) { + mnt->version = oldmnt->version; + + /* FIXME: is this enough to convert uid/gid's ? */ + mnt->mounted_uid = oldmnt->mounted_uid; + mnt->uid = oldmnt->uid; + mnt->gid = oldmnt->gid; + + mnt->file_mode = + oldmnt->file_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + mnt->dir_mode = + oldmnt->dir_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + mnt->file_mode |= S_IFREG; + mnt->dir_mode |= S_IFDIR; + + mnt->flags = (oldmnt->file_mode >> 9); + } else { + if (parse_options(mnt, raw_data)) + goto out_bad_option; + + mnt->mounted_uid = current->uid; + } + smb_setcodepage(&sb->u.smbfs_sb, &mnt->codepage); + if (!sb->u.smbfs_sb.convert) + PARANOIA("convert funcptr was NULL!\n"); + /* * Display the enabled options * Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2) */ - if (mnt->version & SMB_FIX_OLDATTR) + if (mnt->flags & SMB_MOUNT_OLDATTR) printk("SMBFS: Using core getattr (Win 95 speedup)\n"); - else if (mnt->version & SMB_FIX_DIRATTR) + else if (mnt->flags & SMB_MOUNT_DIRATTR) printk("SMBFS: Using dir ff getattr\n"); /* @@ -374,16 +499,18 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent) out_no_root: iput(root_inode); +out_bad_option: kfree(sb->u.smbfs_sb.mnt); out_no_mount: kfree(sb->u.smbfs_sb.temp_buf); out_no_temp: smb_vfree(sb->u.smbfs_sb.packet); out_no_mem: - printk(KERN_ERR "smb_read_super: allocation failure\n"); + if (!sb->u.smbfs_sb.mnt) + printk(KERN_ERR "smb_read_super: allocation failure\n"); goto out_fail; out_wrong_data: - printk(KERN_ERR "SMBFS: need mount version %d\n", SMB_MOUNT_VERSION); + printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver); goto out_fail; out_no_data: printk(KERN_ERR "smb_read_super: missing data argument\n"); diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index e969320b7..7863cd2da 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -16,6 +16,7 @@ #include <linux/fcntl.h> #include <linux/dcache.h> #include <linux/dirent.h> +#include <linux/nls.h> #include <linux/smb_fs.h> #include <linux/smbno.h> @@ -25,10 +26,14 @@ #include "smb_debug.h" + /* Features. Undefine if they cause problems, this should perhaps be a config option. */ #define SMBFS_POSIX_UNLINK 1 +/* Allow smb_retry to be interrupted. Not sure of the benefit ... */ +/* #define SMB_RETRY_INTR */ + #define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) #define SMB_CMD(packet) (*(packet+8)) #define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1)) @@ -48,6 +53,20 @@ static int smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr); + +static inline void +smb_lock_server(struct smb_sb_info *server) +{ + down(&(server->sem)); +} + +static inline void +smb_unlock_server(struct smb_sb_info *server) +{ + up(&(server->sem)); +} + + static void str_upper(char *name, int len) { @@ -83,6 +102,96 @@ static void reverse_string(char *buf, int len) } } +/* no conversion, just a wrapper for memcpy. */ +static int convert_memcpy(char *output, int olen, + const char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to) +{ + memcpy(output, input, ilen); + return ilen; +} + +/* convert from one "codepage" to another (possibly being utf8). */ +static int convert_cp(char *output, int olen, + const char *input, int ilen, + struct nls_table *nls_from, + struct nls_table *nls_to) +{ + int len = 0; + int n; + wchar_t ch; + + if (!nls_from || !nls_to) { + PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to); + return convert_memcpy(output, olen, input, ilen, NULL, NULL); + } + + while (ilen > 0) { + /* convert by changing to unicode and back to the new cp */ + n = nls_from->char2uni((unsigned char *)input, ilen, &ch); + if (n < 0) + goto out; + input += n; + ilen -= n; + + n = nls_to->uni2char(ch, output, olen); + if (n < 0) + goto out; + output += n; + olen -= n; + + len += n; + } +out: + return len; +} + +static int setcodepage(struct smb_sb_info *server, + struct nls_table **p, char *name) +{ + struct nls_table *nls; + + if (!name || !*name) { + nls = NULL; + } else if ( (nls = load_nls(name)) == NULL) { + printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name); + return -EINVAL; + } + + /* if already set, unload the previous one. */ + if (*p) + unload_nls(*p); + *p = nls; + + return 0; +} + +/* Handles all changes to codepage settings. */ +int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp) +{ + int n; + + smb_lock_server(server); + + n = setcodepage(server, &server->local_nls, cp->local_name); + if (n != 0) + goto out; + n = setcodepage(server, &server->remote_nls, cp->remote_name); + if (n != 0) + setcodepage(server, &server->local_nls, NULL); + +out: + if (server->local_nls != NULL && server->remote_nls != NULL) + server->convert = convert_cp; + else + server->convert = convert_memcpy; + + smb_unlock_server(server); + return n; +} + + /*****************************************************************************/ /* */ /* Encoding/Decoding section */ @@ -107,9 +216,11 @@ smb_encode_smb_length(__u8 * p, __u32 len) * smb_build_path: build the path to entry and name storing it in buf. * The path returned will have the trailing '\0'. */ -static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf) +static int smb_build_path(struct smb_sb_info *server, char * buf, + struct dentry * entry, struct qstr * name) { char *path = buf; + int len; if (entry == NULL) goto test_name_and_out; @@ -129,9 +240,16 @@ static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf) * and store it in reversed order [see reverse_string()] */ for (;;) { - memcpy(path, entry->d_name.name, entry->d_name.len); - reverse_string(path, entry->d_name.len); - path += entry->d_name.len; + if (entry->d_name.len > SMB_MAXNAMELEN) + return -ENAMETOOLONG; + if (path - buf + entry->d_name.len > SMB_MAXPATHLEN) + return -ENAMETOOLONG; + + len = server->convert(path, SMB_MAXNAMELEN, + entry->d_name.name, entry->d_name.len, + server->local_nls, server->remote_nls); + reverse_string(path, len); + path += len; *(path++) = '\\'; @@ -147,25 +265,28 @@ test_name_and_out: if (name != NULL) { *(path++) = '\\'; name_and_out: - memcpy(path, name->name, name->len); - path += name->len; + len = server->convert(path, SMB_MAXNAMELEN, + name->name, name->len, + server->local_nls, server->remote_nls); + path += len; } out: *(path++) = '\0'; return (path-buf); } -static char *smb_encode_path(struct smb_sb_info *server, char *buf, - struct dentry *dir, struct qstr *name) +static int smb_encode_path(struct smb_sb_info *server, char *buf, + struct dentry *dir, struct qstr *name) { - char *start = buf; - - buf += smb_build_path(dir, name, buf); + int result; + result = smb_build_path(server, buf, dir, name); + if (result < 0) + goto out; if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) - str_upper(start, buf - start); - - return buf; + str_upper(buf, result); +out: + return result; } /* The following are taken directly from msdos-fs */ @@ -464,18 +585,6 @@ err_unknown: return EIO; } -static inline void -smb_lock_server(struct smb_sb_info *server) -{ - down(&(server->sem)); -} - -static inline void -smb_unlock_server(struct smb_sb_info *server) -{ - up(&(server->sem)); -} - /* * smb_retry: This function should be called when smb_request_ok has * indicated an error. If the error was indicated because the @@ -495,8 +604,7 @@ smb_retry(struct smb_sb_info *server) smb_close_socket(server); - if (pid == 0) - { + if (pid == 0) { printk(KERN_ERR "smb_retry: no connection process\n"); server->state = CONN_RETRIED; goto out; @@ -511,26 +619,35 @@ smb_retry(struct smb_sb_info *server) * Note: use the "priv" flag, as a user process may need to reconnect. */ error = kill_proc(pid, SIGUSR1, 1); - if (error) - { + if (error) { printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error); goto out_restore; } - VERBOSE("signalled pid %d, waiting for new connection\n", - server->conn_pid); + VERBOSE("signalled pid %d, waiting for new connection\n", pid); /* * Wait for the new connection. */ +#ifdef SMB_RETRY_INTR interruptible_sleep_on_timeout(&server->wait, 5*HZ); if (signal_pending(current)) printk(KERN_INFO "smb_retry: caught signal\n"); +#else + /* + * We don't want to be interrupted. For example, what if 'current' + * already has recieved a signal? sleep_on would terminate immediately + * and smbmount would not be able to re-establish connection. + * + * smbmount should be able to reconnect later, but it can't because + * it will get an -EIO on attempts to open the mountpoint! + */ + sleep_on_timeout(&server->wait, 5*HZ); +#endif /* * Check for a valid connection. */ - if (server->state == CONN_VALID) - { + if (server->state == CONN_VALID) { /* This should be changed to VERBOSE, except many smbfs problems is with the userspace daemon not reconnecting. */ PARANOIA("sucessful, new pid=%d, generation=%d\n", @@ -656,7 +773,7 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) if (server->opt.protocol == SMB_PROTOCOL_NT1 && (server->opt.max_xmit < 0x1000) && !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { - server->mnt->version |= SMB_FIX_WIN95; + server->mnt->flags |= SMB_MOUNT_WIN95; #ifdef SMBFS_DEBUG_VERBOSE printk(KERN_NOTICE "smb_newconn: detected WIN95 server\n"); #endif @@ -667,7 +784,11 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt) server->opt.capabilities); out: +#ifdef SMB_RETRY_INTR wake_up_interruptible(&server->wait); +#else + wake_up(&server->wait); +#endif return error; out_putf: @@ -738,7 +859,7 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) { struct inode *ino = dentry->d_inode; int mode, read_write = 0x42, read_only = 0x40; - int error; + int res; char *p; /* @@ -748,6 +869,9 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode = read_only; #if 0 + /* FIXME: why is this code not in? below we fix it so that a caller + wanting RO doesn't get RW. smb_revalidate_inode does some + optimization based on access mode. tail -f needs it to be correct. */ if (!(wish & (O_WRONLY | O_RDWR))) mode = read_only; #endif @@ -757,20 +881,23 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) WSET(server->packet, smb_vwv0, mode); WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); *p++ = 4; - p = smb_encode_path(server, p, dentry, NULL); + res = smb_encode_path(server, p, dentry, NULL); + if (res < 0) + goto out; + p += res; + smb_setup_bcc(server, p); - error = smb_request_ok(server, SMBopen, 7, 0); - if (error != 0) - { + res = smb_request_ok(server, SMBopen, 7, 0); + if (res != 0) { if (smb_retry(server)) goto retry; if (mode == read_write && - (error == -EACCES || error == -ETXTBSY || error == -EROFS)) + (res == -EACCES || res == -ETXTBSY || res == -EROFS)) { VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n", - DENTRY_PATH(dentry), error); + DENTRY_PATH(dentry), res); mode = read_only; goto retry; } @@ -783,10 +910,12 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish) /* smb_vwv2 has mtime */ /* smb_vwv4 has size */ ino->u.smbfs_i.access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK); + if (!(wish & (O_WRONLY | O_RDWR))) + ino->u.smbfs_i.access = SMB_O_RDONLY; ino->u.smbfs_i.open = server->generation; out: - return error; + return res; } /* @@ -1025,7 +1154,7 @@ smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) { struct smb_sb_info *server = server_from_dentry(dentry); char *p; - int error; + int result; smb_lock_server(server); @@ -1034,22 +1163,24 @@ smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid) WSET(server->packet, smb_vwv0, attr); DSET(server->packet, smb_vwv1, utc2local(server, ctime)); *p++ = 4; - p = smb_encode_path(server, p, dentry, NULL); + result = smb_encode_path(server, p, dentry, NULL); + if (result < 0) + goto out; + p += result; smb_setup_bcc(server, p); - error = smb_request_ok(server, SMBcreate, 1, 0); - if (error < 0) - { + result = smb_request_ok(server, SMBcreate, 1, 0); + if (result < 0) { if (smb_retry(server)) goto retry; goto out; } *fileid = WVAL(server->packet, smb_vwv0); - error = 0; + result = 0; out: smb_unlock_server(server); - return error; + return result; } int @@ -1064,14 +1195,22 @@ smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry) retry: p = smb_setup_header(server, SMBmv, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); + *p++ = 4; - p = smb_encode_path(server, p, old_dentry, NULL); + result = smb_encode_path(server, p, old_dentry, NULL); + if (result < 0) + goto out; + p += result; + *p++ = 4; - p = smb_encode_path(server, p, new_dentry, NULL); + result = smb_encode_path(server, p, new_dentry, NULL); + if (result < 0) + goto out; + p += result; + smb_setup_bcc(server, p); - if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) - { + if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) { if (smb_retry(server)) goto retry; goto out; @@ -1097,12 +1236,14 @@ smb_proc_generic_command(struct dentry *dentry, __u8 command) retry: p = smb_setup_header(server, command, 0, 0); *p++ = 4; - p = smb_encode_path(server, p, dentry, NULL); + result = smb_encode_path(server, p, dentry, NULL); + if (result < 0) + goto out; + p += result; smb_setup_bcc(server, p); result = smb_request_ok(server, command, 0, 0); - if (result < 0) - { + if (result < 0) { if (smb_retry(server)) goto retry; goto out; @@ -1165,11 +1306,13 @@ smb_proc_unlink(struct dentry *dentry) p = smb_setup_header(server, SMBunlink, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN); *p++ = 4; - p = smb_encode_path(server, p, dentry, NULL); + result = smb_encode_path(server, p, dentry, NULL); + if (result < 0) + goto out; + p += result; smb_setup_bcc(server, p); - if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) - { + if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) { #if SMBFS_POSIX_UNLINK if (result == -EACCES && !flag) { /* Posix semantics is for the read-only state @@ -1220,8 +1363,7 @@ smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length) *p++ = 0; smb_setup_bcc(server, p); - if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) - { + if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) { if (smb_retry(server)) goto retry; goto out; @@ -1306,6 +1448,8 @@ smb_decode_dirent(struct smb_sb_info *server, __u8 *p, len--; entry->len = len; + /* FIXME: These only work for ascii chars, and recent smbmount doesn't + allow the flag to be set anyway. Remove? */ switch (server->opt.case_handling) { case SMB_CASE_UPPER: str_upper(entry->name, len); @@ -1316,7 +1460,13 @@ smb_decode_dirent(struct smb_sb_info *server, __u8 *p, default: break; } - DEBUG1("len=%d, name=%.*s\n", len, len, entry->name); + + entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN, + entry->name, len, + server->remote_nls, server->local_nls); + entry->name = server->name_buf; + + DEBUG1("len=%d, name=%.*s\n", entry->len, entry->len, entry->name); return p + 22; } @@ -1355,7 +1505,10 @@ retry: WSET(server->packet, smb_vwv1, aDIR); *p++ = 4; if (first == 1) { - p = smb_encode_path(server, p, dir, &mask); + result = smb_encode_path(server, p, dir, &mask); + if (result < 0) + goto unlock_return; + p += result; *p++ = 5; WSET(p, 0, 0); p += 2; @@ -1467,12 +1620,11 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, switch (level) { case 1: len = *((unsigned char *) p + 22); - entry->len = len; entry->name = p + 23; result = p + 24 + len; VERBOSE("info 1 at %p, len=%d, name=%.*s\n", - p, entry->len, entry->len, entry->name); + p, len, len, entry->name); break; case 260: result = p + WVAL(p, 0); @@ -1482,14 +1634,14 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, entry->name = p + 94; if (len && entry->name[len-1] == '\0') len--; - entry->len = len; VERBOSE("info 260 at %p, len=%d, name=%.*s\n", - p, entry->len, entry->len, entry->name); + p, len, len, entry->name); break; default: PARANOIA("Unknown info level %d\n", level); result = p + WVAL(p, 0); + goto out; } switch (server->opt.case_handling) { @@ -1503,6 +1655,11 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, break; } + entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN, + entry->name, len, + server->remote_nls, server->local_nls); + entry->name = server->name_buf; +out: return result; } @@ -1561,7 +1718,12 @@ retry: * Encode the initial path */ mask = param + 12; - mask_len = smb_encode_path(server, mask, dir, &star) - mask; + + mask_len = smb_encode_path(server, mask, dir, &star); + if (mask_len < 0) { + entries = mask_len; + goto unlock_return; + } first = 1; VERBOSE("starting fpos=%d, mask=%s\n", fpos, mask); @@ -1753,7 +1915,11 @@ smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, int mask_len, result; retry: - mask_len = smb_encode_path(server, mask, dentry, NULL) - mask; + mask_len = smb_encode_path(server, mask, dentry, NULL); + if (mask_len < 0) { + result = mask_len; + goto out; + } VERBOSE("name=%s, len=%d\n", mask, mask_len); WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); WSET(param, 2, 1); /* max count */ @@ -1828,7 +1994,10 @@ smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, retry: p = smb_setup_header(server, SMBgetatr, 0, 0); *p++ = 4; - p = smb_encode_path(server, p, dir, NULL); + result = smb_encode_path(server, p, dir, NULL); + if (result < 0) + goto out; + p += result; smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) @@ -1874,7 +2043,10 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - p = smb_encode_path(server, param + 6, dir, NULL); + result = smb_encode_path(server, param + 6, dir, NULL); + if (result < 0) + goto out; + p = param + 6 + result; result = smb_trans2_request(server, TRANSACT2_QPATHINFO, 0, NULL, p - param, param, @@ -1905,7 +2077,7 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, * Kludge alert: Win 95 swaps the date and time field, * contrary to the CIFS docs and Win NT practice. */ - if (server->mnt->version & SMB_FIX_WIN95) { + if (server->mnt->flags & SMB_MOUNT_WIN95) { off_date = 2; off_time = 0; } @@ -1945,21 +2117,16 @@ smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, /* * Select whether to use core or trans2 getattr. + * Win 95 appears to break with the trans2 getattr. */ - if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) { - /* - * Win 95 appears to break with the trans2 getattr. - * Note: mnt->version options are set at mount time (inode.c) - */ - if (server->mnt->version & (SMB_FIX_OLDATTR|SMB_FIX_WIN95)) - goto core_attr; - if (server->mnt->version & SMB_FIX_DIRATTR) + if (server->opt.protocol < SMB_PROTOCOL_LANMAN2 || + (server->mnt->flags & (SMB_MOUNT_OLDATTR|SMB_MOUNT_WIN95)) ) { + result = smb_proc_getattr_core(server, dir, fattr); + } else { + if (server->mnt->flags & SMB_MOUNT_DIRATTR) result = smb_proc_getattr_ff(server, dir, fattr); else result = smb_proc_getattr_trans2(server, dir, fattr); - } else { - core_attr: - result = smb_proc_getattr_core(server, dir, fattr); } smb_finish_dirent(server, fattr); @@ -2008,14 +2175,16 @@ smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, WSET(server->packet, smb_vwv6, 0); WSET(server->packet, smb_vwv7, 0); *p++ = 4; - p = smb_encode_path(server, p, dentry, NULL); + result = smb_encode_path(server, p, dentry, NULL); + if (result < 0) + goto out; + p += result; *p++ = 4; *p++ = 0; smb_setup_bcc(server, p); result = smb_request_ok(server, SMBsetatr, 0, 0); - if (result < 0) - { + if (result < 0) { if (smb_retry(server)) goto retry; goto out; @@ -2073,8 +2242,7 @@ smb_proc_setattr_ext(struct smb_sb_info *server, #endif result = smb_request_ok(server, SMBsetattrE, 0, 0); - if (result < 0) - { + if (result < 0) { if (smb_retry(server)) goto retry; goto out; @@ -2107,7 +2275,10 @@ smb_proc_setattr_trans2(struct smb_sb_info *server, retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - p = smb_encode_path(server, param + 6, dir, NULL); + result = smb_encode_path(server, param + 6, dir, NULL); + if (result < 0) + goto out; + p = param + 6 + result; WSET(data, 0, 0); /* creation time */ WSET(data, 2, 0); @@ -2170,8 +2341,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) smb_lock_server(server); /* setting the time on a Win95 server fails (tridge) */ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 && - !(server->mnt->version & SMB_FIX_WIN95)) - { + !(server->mnt->flags & SMB_MOUNT_WIN95)) { if (smb_is_open(inode) && inode->u.smbfs_i.access != SMB_O_RDONLY) result = smb_proc_setattr_ext(server, inode, fattr); @@ -2182,8 +2352,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) * Fail silently on directories ... timestamp can't be set? */ result = 0; - if (S_ISREG(inode->i_mode)) - { + if (S_ISREG(inode->i_mode)) { /* * Set the mtime by opening and closing the file. * Note that the file is opened read-only, but this @@ -2192,8 +2361,7 @@ smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr) result = -EACCES; if (!smb_is_open(inode)) smb_proc_open(server, dentry, SMB_O_RDONLY); - if (smb_is_open(inode)) - { + if (smb_is_open(inode)) { inode->i_mtime = fattr->f_mtime; result = smb_proc_close_inode(server, inode); } @@ -2208,7 +2376,7 @@ int smb_proc_dskattr(struct super_block *sb, struct statfs *attr) { struct smb_sb_info *server = &(sb->u.smbfs_sb); - int error; + int result; char *p; smb_lock_server(server); @@ -2216,8 +2384,7 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr) retry: smb_setup_header(server, SMBdskattr, 0, 0); - if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) - { + if ((result = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) { if (smb_retry(server)) goto retry; goto out; @@ -2226,11 +2393,11 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr) attr->f_blocks = WVAL(p, 0); attr->f_bsize = WVAL(p, 2) * WVAL(p, 4); attr->f_bavail = attr->f_bfree = WVAL(p, 6); - error = 0; + result = 0; out: smb_unlock_server(server); - return error; + return result; } int diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c index 0c52bf871..01ae6ec87 100644 --- a/fs/smbfs/sock.c +++ b/fs/smbfs/sock.c @@ -111,12 +111,16 @@ smb_data_callback(void* ptr) unsigned char peek_buf[4]; int result; mm_segment_t fs; + int count = 100; /* this is a lot, we should have some data waiting */ + int found = 0; fs = get_fs(); set_fs(get_ds()); lock_kernel(); - while (1) { + while (count-- > 0) { + peek_buf[0] = 0; + result = -EIO; if (job->sk->dead) { PARANOIA("sock dead!\n"); @@ -125,7 +129,7 @@ smb_data_callback(void* ptr) result = _recvfrom(socket, (void *) peek_buf, 1, MSG_PEEK | MSG_DONTWAIT); - if (result == -EAGAIN) + if (result < 0) break; if (peek_buf[0] != 0x85) break; @@ -136,13 +140,15 @@ smb_data_callback(void* ptr) DEBUG1("got SESSION KEEPALIVE\n"); - if (result == -EAGAIN) + if (result < 0) break; + found = 1; } unlock_kernel(); set_fs(fs); - if (result != -EAGAIN) + DEBUG1("found=%d, count=%d, result=%d\n", found, count, result); + if (found) found_data(job->sk); kfree(ptr); } @@ -52,6 +52,10 @@ static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf) SET_OLDSTAT_UID(tmp, inode->i_uid); SET_OLDSTAT_GID(tmp, inode->i_gid); tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); +#if BITS_PER_LONG == 32 + if (inode->i_size > 0x7fffffff) + return -EOVERFLOW; +#endif tmp.st_size = inode->i_size; tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; @@ -74,6 +78,10 @@ static int cp_new_stat(struct inode * inode, struct stat * statbuf) SET_STAT_UID(tmp, inode->i_uid); SET_STAT_GID(tmp, inode->i_gid); tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); +#if BITS_PER_LONG == 32 + if (inode->i_size > 0x7fffffff) + return -EOVERFLOW; +#endif tmp.st_size = inode->i_size; tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; diff --git a/fs/super.c b/fs/super.c index 8576d79d0..81a3fafc2 100644 --- a/fs/super.c +++ b/fs/super.c @@ -10,13 +10,12 @@ * - umount system call * - ustat system call * - * Added options to /proc/mounts - * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996. - * * GK 2/5/95 - Changed to support mounting the root fs via NFS * * Added kerneld support: Jacques Gelinas and Bjorn Ekwall * Added change_root: Werner Almesberger & Hans Lermen, Feb '96 + * Added options to /proc/mounts: + * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996. * Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998 * Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000 */ @@ -428,6 +427,33 @@ static void remove_vfsmnt(struct vfsmount *mnt) kfree(mnt); } + +/* Use octal escapes, like mount does, for embedded spaces etc. */ +static unsigned char need_escaping[] = { ' ', '\t', '\n', '\\' }; + +static int +mangle(const unsigned char *s, char *buf, int len) { + char *sp; + int n; + + sp = buf; + while(*s && sp-buf < len-3) { + for (n = 0; n < sizeof(need_escaping); n++) { + if (*s == need_escaping[n]) { + *sp++ = '\\'; + *sp++ = '0' + ((*s & 0300) >> 6); + *sp++ = '0' + ((*s & 070) >> 3); + *sp++ = '0' + (*s & 07); + goto next; + } + } + *sp++ = *s; + next: + s++; + } + return sp - buf; /* no trailing NUL */ +} + static struct proc_fs_info { int flag; char *str; @@ -466,27 +492,32 @@ int get_filesystem_info( char *buf ) struct proc_fs_info *fs_infop; struct proc_nfs_info *nfs_infop; struct nfs_server *nfss; - int len = 0; - char *path,*buffer = (char *) __get_free_page(GFP_KERNEL); + int len, prevlen; + char *path, *buffer = (char *) __get_free_page(GFP_KERNEL); if (!buffer) return 0; - for (p = vfsmntlist.next; p!=&vfsmntlist && len < PAGE_SIZE - 160; - p = p->next) { + len = prevlen = 0; + +#define FREEROOM ((int)PAGE_SIZE-200-len) +#define MANGLE(s) len += mangle((s), buf+len, FREEROOM); + + for (p = vfsmntlist.next; p != &vfsmntlist; p = p->next) { struct vfsmount *tmp = list_entry(p, struct vfsmount, mnt_list); if (!(tmp->mnt_flags & MNT_VISIBLE)) continue; path = d_path(tmp->mnt_root, tmp, buffer, PAGE_SIZE); if (!path) continue; - len += sprintf( buf + len, "%s %s %s %s", - tmp->mnt_devname ? tmp->mnt_devname : "none", path, - tmp->mnt_sb->s_type->name, - tmp->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw" ); + MANGLE(tmp->mnt_devname ? tmp->mnt_devname : "none"); + buf[len++] = ' '; + MANGLE(path); + buf[len++] = ' '; + MANGLE(tmp->mnt_sb->s_type->name); + len += sprintf(buf+len, " %s", + tmp->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw"); for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { - if (tmp->mnt_sb->s_flags & fs_infop->flag) { - strcpy(buf + len, fs_infop->str); - len += strlen(fs_infop->str); - } + if (tmp->mnt_sb->s_flags & fs_infop->flag) + MANGLE(fs_infop->str); } if (!strcmp("nfs", tmp->mnt_sb->s_type->name)) { nfss = &tmp->mnt_sb->u.nfs_sb.s_server; @@ -527,17 +558,24 @@ int get_filesystem_info( char *buf ) str = nfs_infop->str; else str = nfs_infop->nostr; - strcpy(buf + len, str); - len += strlen(str); + MANGLE(str); } - len += sprintf(buf+len, ",addr=%s", - nfss->hostname); + len += sprintf(buf+len, ",addr="); + MANGLE(nfss->hostname); + } + len += sprintf(buf + len, " 0 0\n"); + if (FREEROOM <= 3) { + len = prevlen; + len += sprintf(buf+len, "# truncated\n"); + break; } - len += sprintf( buf + len, " 0 0\n" ); + prevlen = len; } free_page((unsigned long) buffer); return len; +#undef MANGLE +#undef FREEROOM } /** @@ -775,7 +813,8 @@ static struct super_block *get_sb_bdev(struct file_system_type *fs_type, dev = to_kdev_t(bdev->bd_dev); sb = get_super(dev); if (sb) { - if (fs_type == sb->s_type) { + if (fs_type == sb->s_type && + ((flags ^ sb->s_flags) & MS_RDONLY) == 0) { path_release(&nd); return sb; } @@ -1090,7 +1129,7 @@ asmlinkage long sys_umount(char * name, int flags) if (retval) goto out; retval = -EINVAL; - if (nd.dentry!=nd.mnt->mnt_root) + if (nd.dentry != nd.mnt->mnt_root) goto dput_and_out; retval = -EPERM; @@ -1263,8 +1302,8 @@ static int copy_mount_options (const void *data, unsigned long *where) * PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent * information (or be NULL). * - * NOTE! As old versions of mount() didn't use this setup, the flags - * have to have a special 16-bit magic number in the high word: + * NOTE! As pre-0.97 versions of mount() didn't use this setup, the + * flags have to have a special 16-bit magic number in the high word: * 0xC0ED. If this magic word isn't present, the flags and data info * aren't used, as the syscall assumes we are talking to an older * version that didn't understand them. @@ -1736,7 +1775,7 @@ int __init change_root(kdev_t new_root_dev,const char *put_old) printk("okay\n"); return 0; } - printk(KERN_ERR "error %d\n",blivet); + printk(KERN_ERR "error %d\n", blivet); return error; } /* FIXME: we should hold i_zombie on nd.dentry */ diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 57968a839..5cdf5c605 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -61,7 +61,7 @@ static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) inode->i_ino, (off_t) filp->f_pos, sde.inode); i = strnlen(sde.name, SYSV_NAMELEN); - if (filldir(dirent, sde.name, i, filp->f_pos, sde.inode) < 0) { + if (filldir(dirent, sde.name, i, filp->f_pos, sde.inode, DT_UNKNOWN) < 0) { brelse(bh); return 0; } diff --git a/fs/udf/dir.c b/fs/udf/dir.c index 09121f0ef..9f093c536 100644 --- a/fs/udf/dir.c +++ b/fs/udf/dir.c @@ -94,7 +94,7 @@ int udf_readdir(struct file *filp, void *dirent, filldir_t filldir) if ( filp->f_pos == 0 ) { - if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino)) + if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR)) return 0; } @@ -206,7 +206,7 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d if (!lfi) /* parent directory */ { - if (filldir(dirent, "..", 2, filp->f_pos, filp->f_dentry->d_parent->d_inode->i_ino)) + if (filldir(dirent, "..", 2, filp->f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR)) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); @@ -219,7 +219,7 @@ do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *d { if ((flen = udf_get_filename(nameptr, fname, lfi))) { - if (filldir(dirent, fname, flen, filp->f_pos, iblock)) + if (filldir(dirent, fname, flen, filp->f_pos, iblock, DT_UNKNOWN)) { if (fibh.sbh != fibh.ebh) udf_release_data(fibh.ebh); diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 08fa1f4bf..755198ded 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -122,11 +122,14 @@ revalidate: * not the directory has been modified * during the copy operation. */ unsigned long version = filp->f_version; + unsigned char d_type = DT_UNKNOWN; UFSD(("filldir(%s,%u)\n", de->d_name, SWAB32(de->d_ino))) UFSD(("namlen %u\n", ufs_get_de_namlen(de))) + if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) + d_type = de->d_u.d_44.d_type; error = filldir(dirent, de->d_name, ufs_get_de_namlen(de), - filp->f_pos, SWAB32(de->d_ino)); + filp->f_pos, SWAB32(de->d_ino), d_type); if (error) break; if (version != filp->f_version) diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c index a84b8891f..b5c14c221 100644 --- a/fs/umsdos/dir.c +++ b/fs/umsdos/dir.c @@ -76,7 +76,7 @@ static int umsdos_dir_once ( void *buf, if (d->count == 0) { PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", len, name, offset)); - ret = d->filldir (d->dirbuf, name, len, offset, ino); + ret = d->filldir (d->dirbuf, name, len, offset, ino, DT_UNKNOWN); d->stop = ret < 0; d->count = 1; } @@ -120,7 +120,7 @@ static int umsdos_readdir_x (struct inode *dir, struct file *filp, Printk ((KERN_WARNING "umsdos_readdir_x: pseudo_root thing UMSDOS_SPECIAL_DIRFPOS\n")); if (filldir (dirbuf, "DOS", 3, - UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO) == 0) { + UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO, DT_DIR) == 0) { filp->f_pos++; } goto out_end; @@ -235,7 +235,7 @@ dret->d_parent->d_name.name, dret->d_name.name); */ if (inode != pseudo_root && !(entry.flags & UMSDOS_HIDDEN)) { if (filldir (dirbuf, entry.name, entry.name_len, - cur_f_pos, inode->i_ino) < 0) { + cur_f_pos, inode->i_ino, DT_UNKNOWN) < 0) { pos = cur_f_pos; } Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n", diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c index a477ade2c..ff208e6ac 100644 --- a/fs/umsdos/rdir.c +++ b/fs/umsdos/rdir.c @@ -33,7 +33,8 @@ static int rdir_filldir ( void *buf, const char *name, int name_len, off_t offset, - ino_t ino) + ino_t ino, + unsigned int d_type) { int ret = 0; struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR *) buf; @@ -48,11 +49,11 @@ static int rdir_filldir ( void *buf, /* Make sure the .. entry points back to the pseudo_root */ ino = pseudo_root->i_ino; } - ret = d->filldir (d->dirbuf, name, name_len, offset, ino); + ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN); } } else { /* Any DOS directory */ - ret = d->filldir (d->dirbuf, name, name_len, offset, ino); + ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN); } return ret; } |