*/
#include <linux/types.h>
+#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/fs.h>
static struct smb_ops smb_ops_win95;
static struct smb_ops smb_ops_winNT;
static struct smb_ops smb_ops_unix;
+static struct smb_ops smb_ops_null;
static void
smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
static int
smb_proc_setattr_ext(struct smb_sb_info *server,
struct inode *inode, struct smb_fattr *fattr);
-int
+static int
smb_proc_query_cifsunix(struct smb_sb_info *server);
static void
install_ops(struct smb_ops *dst, struct smb_ops *src);
filp = fget(opt->fd);
if (!filp)
goto out;
- if (!smb_valid_socket(filp->f_dentry->d_inode))
+ if (!smb_valid_socket(filp->f_path.dentry->d_inode))
goto out_putf;
server->sock_file = filp;
- server->conn_pid = current->pid;
+ server->conn_pid = get_pid(task_pid(current));
server->opt = *opt;
server->generation += 1;
server->state = CONN_VALID;
/*
* Store the server in sock user_data (Only used by sunrpc)
*/
- sk = SOCKET_I(filp->f_dentry->d_inode)->sk;
+ sk = SOCKET_I(filp->f_path.dentry->d_inode)->sk;
sk->sk_user_data = server;
/* chain into the data_ready callback */
}
VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n",
- server->opt.protocol, server->opt.max_xmit, server->conn_pid,
- server->opt.capabilities);
+ server->opt.protocol, server->opt.max_xmit,
+ pid_nr(server->conn_pid), server->opt.capabilities);
/* FIXME: this really should be done by smbmount. */
if (server->opt.max_xmit > SMB_MAX_PACKET_SIZE) {
smbiod_wake_up();
if (server->opt.capabilities & SMB_CAP_UNIX)
smb_proc_query_cifsunix(server);
+
+ server->conn_complete++;
+ wake_up_interruptible_all(&server->conn_wq);
return error;
out:
* So we must first calculate the amount of padding used by the server.
*/
data_off -= hdrlen;
- if (data_off > SMB_READX_MAX_PAD) {
- PARANOIA("offset is larger than max pad!\n");
- PARANOIA("%d > %d\n", data_off, SMB_READX_MAX_PAD);
+ if (data_off > SMB_READX_MAX_PAD || data_off < 0) {
+ PARANOIA("offset is larger than SMB_READX_MAX_PAD or negative!\n");
+ PARANOIA("%d > %d || %d < 0\n", data_off, SMB_READX_MAX_PAD, data_off);
req->rq_rlen = req->rq_bufsize + 1;
return;
}
fattr->f_nlink = 1;
fattr->f_uid = server->mnt->uid;
fattr->f_gid = server->mnt->gid;
- fattr->f_blksize = SMB_ST_BLKSIZE;
fattr->f_unix = 0;
}
}
void
-smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
+smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr,
+ struct super_block *sb)
{
smb_init_dirent(server, fattr);
fattr->attr = aDIR;
fattr->f_ino = 2; /* traditional root inode number */
- fattr->f_mtime = CURRENT_TIME;
+ fattr->f_mtime = current_fs_time(sb);
smb_finish_dirent(server, fattr);
}
smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
struct smb_cache_control *ctl)
{
- struct dentry *dir = filp->f_dentry;
+ struct dentry *dir = filp->f_path.dentry;
struct smb_sb_info *server = server_from_dentry(dir);
struct qstr qname;
struct smb_fattr fattr;
return result;
}
-void smb_decode_unix_basic(struct smb_fattr *fattr, char *p)
+static void smb_decode_unix_basic(struct smb_fattr *fattr, struct smb_sb_info *server, char *p)
{
+ u64 size, disk_bytes;
+
/* FIXME: verify nls support. all is sent as utf8? */
fattr->f_unix = 1;
/* 84 L permissions */
/* 92 L link count */
- fattr->f_size = LVAL(p, 0);
- fattr->f_blocks = LVAL(p, 8);
+ size = LVAL(p, 0);
+ disk_bytes = LVAL(p, 8);
+
+ /*
+ * Some samba versions round up on-disk byte usage
+ * to 1MB boundaries, making it useless. When seeing
+ * that, use the size instead.
+ */
+ if (!(disk_bytes & 0xfffff))
+ disk_bytes = size+511;
+
+ fattr->f_size = size;
+ fattr->f_blocks = disk_bytes >> 9;
fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16));
fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24));
fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32));
- fattr->f_uid = LVAL(p, 40);
- fattr->f_gid = LVAL(p, 48);
+
+ if (server->mnt->flags & SMB_MOUNT_UID)
+ fattr->f_uid = server->mnt->uid;
+ else
+ fattr->f_uid = LVAL(p, 40);
+
+ if (server->mnt->flags & SMB_MOUNT_GID)
+ fattr->f_gid = server->mnt->gid;
+ else
+ fattr->f_gid = LVAL(p, 48);
+
fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56));
if (S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) {
fattr->f_rdev = MKDEV(major & 0xffffffff, minor & 0xffffffff);
if (MAJOR(fattr->f_rdev) != (major & 0xffffffff) ||
- MINOR(fattr->f_rdev) != (minor & 0xffffffff))
+ MINOR(fattr->f_rdev) != (minor & 0xffffffff))
fattr->f_rdev = 0;
}
+
fattr->f_mode |= LVAL(p, 84);
+
+ if ( (server->mnt->flags & SMB_MOUNT_DMODE) &&
+ (S_ISDIR(fattr->f_mode)) )
+ fattr->f_mode = (server->mnt->dir_mode & S_IRWXUGO) | S_IFDIR;
+ else if ( (server->mnt->flags & SMB_MOUNT_FMODE) &&
+ !(S_ISDIR(fattr->f_mode)) )
+ fattr->f_mode = (server->mnt->file_mode & S_IRWXUGO) |
+ (fattr->f_mode & S_IFMT);
+
}
/*
/* FIXME: should we check the length?? */
p += 8;
- smb_decode_unix_basic(fattr, p);
+ smb_decode_unix_basic(fattr, server, p);
VERBOSE("info SMB_FIND_FILE_UNIX at %p, len=%d, name=%.*s\n",
p, len, len, qname->name);
break;
smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
struct smb_cache_control *ctl)
{
- struct dentry *dir = filp->f_dentry;
+ struct dentry *dir = filp->f_path.dentry;
struct smb_sb_info *server = server_from_dentry(dir);
struct qstr qname;
struct smb_fattr fattr;
*/
mask = param + 12;
- mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star);
- if (mask_len < 0) {
- result = mask_len;
+ result = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star);
+ if (result <= 0)
goto out_free;
- }
- mask_len--; /* mask_len is strlen, not #bytes */
+ mask_len = result - 1; /* mask_len is strlen, not #bytes */
+ result = 0;
first = 1;
VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask);
- result = 0;
entries_seen = 2;
ff_eos = 0;
if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) {
/* a damn Win95 bug - sometimes it clags if you
ask it too fast */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/5);
+ schedule_timeout_interruptible(msecs_to_jiffies(200));
continue;
}
if (req->rq_rcls != 0) {
result = smb_errno(req);
PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n",
- mask, result, server->rcls, server->err);
+ mask, result, req->rq_rcls, req->rq_err);
break;
}
/*
* Update the mask string for the next message.
*/
- if (mask_len < 0)
- mask_len = 0;
if (mask_len > 255)
mask_len = 255;
if (mask_len)
result = smb_add_request(req);
if (result < 0)
goto out_free;
- if (server->rcls != 0) {
+ if (req->rq_rcls != 0) {
result = smb_errno(req);
#ifdef SMBFS_PARANOIA
if (result != -ENOENT)
result = smb_add_request(req);
if (result < 0)
goto out;
- if (server->rcls != 0) {
+ if (req->rq_rcls != 0) {
VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
¶m[6], result, req->rq_rcls, req->rq_err);
result = smb_errno(req);
if (result < 0)
goto out_free;
- smb_decode_unix_basic(attr, req->rq_data);
+ smb_decode_unix_basic(attr, server, req->rq_data);
out_free:
smb_rput(req);
return result;
}
+static int
+smb_proc_ops_wait(struct smb_sb_info *server)
+{
+ int result;
+
+ result = wait_event_interruptible_timeout(server->conn_wq,
+ server->conn_complete, 30*HZ);
+
+ if (!result || signal_pending(current))
+ return -EIO;
+
+ return 0;
+}
+
static int
smb_proc_getattr_null(struct smb_sb_info *server, struct dentry *dir,
- struct smb_fattr *attr)
+ struct smb_fattr *fattr)
{
- return -EIO;
+ int result;
+
+ if (smb_proc_ops_wait(server) < 0)
+ return -EIO;
+
+ smb_init_dirent(server, fattr);
+ result = server->ops->getattr(server, dir, fattr);
+ smb_finish_dirent(server, fattr);
+
+ return result;
+}
+
+static int
+smb_proc_readdir_null(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctl)
+{
+ struct smb_sb_info *server = server_from_dentry(filp->f_path.dentry);
+
+ if (smb_proc_ops_wait(server) < 0)
+ return -EIO;
+
+ return server->ops->readdir(filp, dirent, filldir, ctl);
}
int
LSET(data, 32, SMB_TIME_NO_CHANGE);
LSET(data, 40, SMB_UID_NO_CHANGE);
LSET(data, 48, SMB_GID_NO_CHANGE);
- LSET(data, 56, smb_filetype_from_mode(attr->ia_mode));
+ DSET(data, 56, smb_filetype_from_mode(attr->ia_mode));
LSET(data, 60, major);
LSET(data, 68, minor);
LSET(data, 76, 0);
}
int
-smb_proc_dskattr(struct super_block *sb, struct kstatfs *attr)
+smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr)
{
- struct smb_sb_info *server = SMB_SB(sb);
+ struct smb_sb_info *server = SMB_SB(dentry->d_sb);
int result;
char *p;
long unit;
if (result < 0)
goto out_free;
DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
- ¶m[6], result, server->rcls, server->err);
+ ¶m[6], result, req->rq_rcls, req->rq_err);
/* copy data up to the \0 or buffer length */
result = len;
goto out_free;
DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
- ¶m[6], result, server->rcls, server->err);
+ ¶m[6], result, req->rq_rcls, req->rq_err);
result = 0;
out_free:
goto out_free;
DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
- ¶m[6], result, server->rcls, server->err);
+ ¶m[6], result, req->rq_rcls, req->rq_err);
result = 0;
out_free:
return result;
}
-int
+static int
smb_proc_query_cifsunix(struct smb_sb_info *server)
{
int result;
/* Place holder until real ops are in place */
static struct smb_ops smb_ops_null =
{
+ .readdir = smb_proc_readdir_null,
.getattr = smb_proc_getattr_null,
};