/*
* inode.c
*
* Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
*
*/
#include <linux/module.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <linux/sched.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
#include <linux/fcntl.h>
#include <linux/malloc.h>
extern int close_fp(struct file *filp);
static void smb_put_inode(struct inode *);
static void smb_read_inode(struct inode *);
static void smb_put_super(struct super_block *);
static void smb_statfs(struct super_block *, struct statfs *, int bufsiz);
static struct super_operations smb_sops =
{
smb_read_inode, /* read inode */
smb_notify_change, /* notify change */
NULL, /* write inode */
smb_put_inode, /* put inode */
smb_put_super, /* put superblock */
NULL, /* write superblock */
smb_statfs, /* stat filesystem */
NULL
};
/* smb_read_inode: Called from iget, it only traverses the allocated
smb_inode_info's and initializes the inode from the data found
there. It does not allocate or deallocate anything. */
static void
smb_read_inode(struct inode *inode)
{
/* Our task should be extremely simple here. We only have to
look up the information somebody else (smb_iget) put into
the inode tree. */
struct smb_server *server = SMB_SERVER(inode);
struct smb_inode_info *inode_info
= smb_find_inode(server, inode->i_ino);
if (inode_info == NULL)
{
/* Ok, now we're in trouble. The inode info is not
there. What should we do now??? */
printk("smb_read_inode: inode %ld info not found\n",
inode->i_ino);
return;
}
inode_info->state = SMB_INODE_VALID;
SMB_INOP(inode) = inode_info;
inode->i_mode = inode_info->finfo.f_mode;
inode->i_nlink = inode_info->finfo.f_nlink;
inode->i_uid = inode_info->finfo.f_uid;
inode->i_gid = inode_info->finfo.f_gid;
inode->i_rdev = inode_info->finfo.f_rdev;
inode->i_size = inode_info->finfo.f_size;
inode->i_mtime = inode_info->finfo.f_mtime;
inode->i_ctime = inode_info->finfo.f_ctime;
inode->i_atime = inode_info->finfo.f_atime;
inode->i_blksize = inode_info->finfo.f_blksize;
inode->i_blocks = inode_info->finfo.f_blocks;
if (S_ISREG(inode->i_mode))
{
inode->i_op = &smb_file_inode_operations;
} else if (S_ISDIR(inode->i_mode))
{
inode->i_op = &smb_dir_inode_operations;
} else
{
inode->i_op = NULL;
}
}
static void
smb_put_inode(struct inode *inode)
{
struct smb_server *server = SMB_SERVER(inode);
struct smb_inode_info *info = SMB_INOP(inode);
struct smb_dirent *finfo;
__u32 mtime = inode->i_mtime;
if (inode->i_count > 1) {
printk("smb_put_inode: in use device %s, inode %ld count=%d\n",
kdevname(inode->i_dev), inode->i_ino, inode->i_count);
return;
}
if (S_ISDIR(inode->i_mode))
{
smb_invalid_dir_cache(inode->i_ino);
}
clear_inode(inode);
/*
* We don't want the inode to be reused as free if we block here,
* so temporarily increment i_count.
*/
inode->i_count++;
if (info) {
finfo = &info->finfo;
if (finfo->opened != 0)
{
if (smb_proc_close(server, finfo->fileid, mtime))
{
/* We can't do anything but complain. */
printk("smb_put_inode: could not close\n");
}
}
smb_free_inode_info(info);
} else
printk("smb_put_inode: no inode info??\n");
inode->i_count--;
}
static void
smb_put_super(struct super_block *sb)
{
struct smb_server *server = &(SMB_SBP(sb)->s_server);
smb_proc_disconnect(server);
smb_dont_catch_keepalive(server);
close_fp(server->sock_file);
lock_super(sb);
smb_free_all_inodes(server);
smb_vfree(server->packet);
server->packet = NULL;
sb->s_dev = 0;
smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
unlock_super(sb);
MOD_DEC_USE_COUNT;
}
struct smb_mount_data_v4
{
int version;
unsigned int fd;
uid_t mounted_uid;
struct sockaddr_in addr;
char server_name[17];
char client_name[17];
char service[64];
char root_path[64];
char username[64];
char password[64];
unsigned short max_xmit;
uid_t uid;
gid_t gid;
mode_t file_mode;
mode_t dir_mode;
};
static int
smb_get_mount_data(struct smb_mount_data *target, void *source)
{
struct smb_mount_data_v4 *v4 = (struct smb_mount_data_v4 *) source;
struct smb_mount_data *cur = (struct smb_mount_data *) source;
if (source == NULL)
{
return 1;
}
if (cur->version == SMB_MOUNT_VERSION)
{
memcpy(target, cur, sizeof(struct smb_mount_data));
return 0;
}
if (v4->version == 4)
{
target->version = 5;
target->fd = v4->fd;
target->mounted_uid = v4->mounted_uid;
target->addr = v4->addr;
memcpy(target->server_name, v4->server_name, 17);
memcpy(target->client_name, v4->client_name, 17);
memcpy(target->service, v4->service, 64);
memcpy(target->root_path, v4->root_path, 64);
memcpy(target->username, v4->username, 64);
memcpy(target->password, v4->password, 64);
target->max_xmit = v4->max_xmit;
target->uid = v4->uid;
target->gid = v4->gid;
target->file_mode = v4->file_mode;
target->dir_mode = v4->dir_mode;
memset(target->domain, 0, 64);
strcpy(target->domain, "?");
return 0;
}
return 1;
}
struct super_block *
smb_read_super(struct super_block *sb, void *raw_data, int silent)
{
struct smb_mount_data data;
struct smb_server *server;
struct smb_sb_info *smb_sb;
unsigned int fd;
struct file *filp;
kdev_t dev = sb->s_dev;
int error;
MOD_INC_USE_COUNT;
if (smb_get_mount_data(&data, raw_data) != 0)
{
printk("smb_read_super: wrong data argument\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
fd = data.fd;
if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
{
printk("smb_read_super: invalid file descriptor\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
if (!S_ISSOCK(filp->f_inode->i_mode))
{
printk("smb_read_super: not a socket!\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
/* We must malloc our own super-block info */
smb_sb = (struct smb_sb_info *) smb_kmalloc(sizeof(struct smb_sb_info),
GFP_KERNEL);
if (smb_sb == NULL)
{
printk("smb_read_super: could not alloc smb_sb_info\n");
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
}
filp->f_count += 1;
lock_super(sb);
SMB_SBP(sb) = smb_sb;
sb->s_blocksize = 1024; /* Eh... Is this correct? */
sb->s_blocksize_bits = 10;
sb->s_magic = SMB_SUPER_MAGIC;
sb->s_dev = dev;
sb->s_op = &smb_sops;
server = &(SMB_SBP(sb)->s_server);
server->sock_file = filp;
server->lock = 0;
server->wait = NULL;
server->packet = NULL;
server->max_xmit = data.max_xmit;
if (server->max_xmit <= 0)
{
server->max_xmit = SMB_DEF_MAX_XMIT;
}
server->tid = 0;
server->pid = current->pid;
server->mid = current->pid + 20;
server->m = data;
server->m.file_mode = (server->m.file_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
server->m.dir_mode = (server->m.dir_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
smb_init_root(server);
error = smb_proc_connect(server);
unlock_super(sb);
if (error < 0)
{
sb->s_dev = 0;
DPRINTK("smb_read_super: Failed connection, bailing out "
"(error = %d).\n", -error);
goto fail;
}
if (server->protocol >= PROTOCOL_LANMAN2)
{
server->case_handling = CASE_DEFAULT;
} else
{
server->case_handling = CASE_LOWER;
}
if ((error = smb_proc_dskattr(sb, &(SMB_SBP(sb)->s_attr))) < 0)
{
sb->s_dev = 0;
printk("smb_read_super: could not get super block "
"attributes\n");
goto fail;
}
smb_init_root_dirent(server, &(server->root.finfo));
if (!(sb->s_mounted = iget(sb, smb_info_ino(&(server->root)))))
{
sb->s_dev = 0;
printk("smb_read_super: get root inode failed\n");
goto fail;
}
return sb;
fail:
if (server->packet != NULL)
{
smb_vfree(server->packet);
server->packet = NULL;
}
filp->f_count -= 1;
smb_dont_catch_keepalive(server);
smb_kfree_s(SMB_SBP(sb), sizeof(struct smb_sb_info));
MOD_DEC_USE_COUNT;
return NULL;
}
static void
smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
int error;
struct smb_dskattr attr;
struct statfs tmp;
error = smb_proc_dskattr(sb, &attr);
if (error)
{
printk("smb_statfs: dskattr error = %d\n", -error);
attr.total = attr.allocblocks = attr.blocksize =
attr.free = 0;
}
tmp.f_type = SMB_SUPER_MAGIC;
tmp.f_bsize = attr.blocksize * attr.allocblocks;
tmp.f_blocks = attr.total;
tmp.f_bfree = attr.free;
tmp.f_bavail = attr.free;
tmp.f_files = -1;
tmp.f_ffree = -1;
tmp.f_namelen = SMB_MAXPATHLEN;
memcpy_tofs(buf, &tmp, bufsiz);
}
int
smb_notify_change(struct inode *inode, struct iattr *attr)
{
int error = 0;
if ((error = inode_change_ok(inode, attr)) < 0)
return error;
if (((attr->ia_valid & ATTR_UID) &&
(attr->ia_uid != SMB_SERVER(inode)->m.uid)))
return -EPERM;
if (((attr->ia_valid & ATTR_GID) &&
(attr->ia_gid != SMB_SERVER(inode)->m.gid)))
return -EPERM;
if (attr->ia_valid & ATTR_MODE) {
struct smb_dirent *fold = SMB_FINFO(inode);
struct smb_dirent finfo;
if (attr->ia_mode & ~(S_IFREG | S_IFDIR |
S_IRWXU | S_IRWXG | S_IRWXO))
return -EPERM;
memset((char *)&finfo, 0, sizeof(finfo));
finfo.attr = fold->attr;
if((attr->ia_mode & 0200) == 0)
finfo.attr |= aRONLY;
else
finfo.attr &= ~aRONLY;
if ((error = smb_proc_setattr(SMB_SERVER(inode),
inode, &finfo)) >= 0)
{
fold->attr = finfo.attr;
if ((attr->ia_mode & 0200) == 0)
inode->i_mode &= ~0222;
else
inode->i_mode |= 0222;
}
}
if ((attr->ia_valid & ATTR_SIZE) != 0)
{
if ((error = smb_make_open(inode, O_WRONLY)) < 0)
goto fail;
if ((error = smb_proc_trunc(SMB_SERVER(inode),
SMB_FINFO(inode)->fileid,
attr->ia_size)) < 0)
goto fail;
}
/* ATTR_CTIME and ATTR_ATIME can not be set via SMB, so ignore it. */
if (attr->ia_valid & ATTR_MTIME)
{
if (smb_make_open(inode, O_WRONLY) != 0)
error = -EACCES;
else
inode->i_mtime = attr->ia_mtime;
}
fail:
smb_invalid_dir_cache(smb_info_ino(SMB_INOP(inode)->dir));
return error;
}
#ifdef DEBUG_SMB_MALLOC
int smb_malloced;
int smb_current_kmalloced;
int smb_current_vmalloced;
#endif
static struct file_system_type smb_fs_type =
{
smb_read_super, "smbfs", 0, NULL
};
int
init_smb_fs(void)
{
return register_filesystem(&smb_fs_type);
}
#ifdef MODULE
int
init_module(void)
{
int status;
DPRINTK("smbfs: init_module called\n");
#ifdef DEBUG_SMB_MALLOC
smb_malloced = 0;
smb_current_kmalloced = 0;
smb_current_vmalloced = 0;
#endif
smb_init_dir_cache();
if ((status = init_smb_fs()) == 0)
register_symtab(0);
return status;
}
void
cleanup_module(void)
{
DPRINTK("smbfs: cleanup_module called\n");
smb_free_dir_cache();
unregister_filesystem(&smb_fs_type);
#ifdef DEBUG_SMB_MALLOC
printk("smb_malloced: %d\n", smb_malloced);
printk("smb_current_kmalloced: %d\n", smb_current_kmalloced);
printk("smb_current_vmalloced: %d\n", smb_current_vmalloced);
#endif
}
#endif