Commit 62c4f318 authored by Urban Widmark's avatar Urban Widmark Committed by Linus Torvalds

[PATCH] SMB Unix Extensions

This patch adds symlinks, hardlinks, device nodes, uid/gid, unix
permissions vs servers that support it (ie samba). Most of this is the
work of John Newbigin, I just modified it for 2.5.

There are issues with what samba allows (eg you can't make arbitrary
symlinks) and room for improvements (use the servers value for ino?). But
it doesn't affect "normal" users.
parent add28398
ChangeLog for smbfs.
2002-04-19 John Newbigin <jn@it.swin.edu.au>
* Implementation of CIFS Extensions for UNIX systems, including soft
and hard links.
2001-08-03 Urban Widmark <urban@teststation.com>
* *.c: Unicode support
......
......@@ -5,7 +5,7 @@
obj-$(CONFIG_SMB_FS) += smbfs.o
smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o \
smbiod.o request.o
symlink.o smbiod.o request.o
# If you want debugging output, you may add these flags to the EXTRA_CFLAGS
# SMBFS_PARANOIA should normally be enabled.
......@@ -24,7 +24,8 @@ include $(TOPDIR)/Rules.make
#
# getopt.c not included. It is intentionally separate
SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c smbiod.c request.c
SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c smbiod.c request.c \
symlink.c
proto:
-rm -f proto.h
......
......@@ -30,6 +30,8 @@ static int smb_rmdir(struct inode *, struct dentry *);
static int smb_unlink(struct inode *, struct dentry *);
static int smb_rename(struct inode *, struct dentry *,
struct inode *, struct dentry *);
static int smb_make_node(struct inode *,struct dentry *,int,int);
static int smb_link(struct dentry *, struct inode *, struct dentry *);
struct file_operations smb_dir_operations =
{
......@@ -51,6 +53,21 @@ struct inode_operations smb_dir_inode_operations =
.setattr = smb_notify_change,
};
struct inode_operations smb_dir_inode_operations_unix =
{
.create = smb_create,
.lookup = smb_lookup,
.unlink = smb_unlink,
.mkdir = smb_mkdir,
.rmdir = smb_rmdir,
.rename = smb_rename,
.getattr = smb_getattr,
.setattr = smb_notify_change,
.symlink = smb_symlink,
.mknod = smb_make_node,
.link = smb_link,
};
/*
* Read a directory, using filldir to fill the dirent memory.
* smb_proc_readdir does the actual reading from the smb server.
......@@ -485,8 +502,10 @@ smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id)
static int
smb_create(struct inode *dir, struct dentry *dentry, int mode)
{
struct smb_sb_info *server = server_from_dentry(dentry);
__u16 fileid;
int error;
struct iattr attr;
VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode);
......@@ -494,6 +513,12 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode)
smb_invalid_dir_cache(dir);
error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid);
if (!error) {
if (server->opt.capabilities & SMB_CAP_UNIX) {
/* Set attributes for new file */
attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode;
error = smb_proc_setattr_unix(dentry, &attr, 0, 0);
}
error = smb_instantiate(dentry, fileid, 1);
} else {
PARANOIA("%s/%s failed, error=%d\n",
......@@ -507,12 +532,20 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode)
static int
smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct smb_sb_info *server = server_from_dentry(dentry);
int error;
struct iattr attr;
lock_kernel();
smb_invalid_dir_cache(dir);
error = smb_proc_mkdir(dentry);
if (!error) {
if (server->opt.capabilities & SMB_CAP_UNIX) {
/* Set attributes for new directory */
attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode;
error = smb_proc_setattr_unix(dentry, &attr, 0, 0);
}
error = smb_instantiate(dentry, 0, 0);
}
unlock_kernel();
......@@ -601,3 +634,46 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry,
unlock_kernel();
return error;
}
/*
* FIXME: samba servers won't let you create device nodes unless uid/gid
* matches the connection credentials (and we don't know which those are ...)
*/
static int
smb_make_node(struct inode *dir, struct dentry *dentry, int mode, int dev)
{
int error;
struct iattr attr;
attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
attr.ia_mode = mode;
attr.ia_uid = current->euid;
attr.ia_gid = current->egid;
smb_invalid_dir_cache(dir);
error = smb_proc_setattr_unix(dentry, &attr, MAJOR(dev), MINOR(dev));
if (!error) {
error = smb_instantiate(dentry, 0, 0);
}
return error;
}
/*
* dentry = existing file
* new_dentry = new file
*/
static int
smb_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry)
{
int error;
DEBUG1("smb_link old=%s/%s new=%s/%s\n",
DENTRY_PATH(dentry), DENTRY_PATH(new_dentry));
smb_invalid_dir_cache(dir);
error = smb_proc_link(server_from_dentry(dentry), dentry, new_dentry);
if (!error) {
smb_renew_times(dentry);
error = smb_instantiate(new_dentry, 0, 0);
}
return error;
}
......@@ -106,6 +106,7 @@ static struct super_operations smb_sops =
struct inode *
smb_iget(struct super_block *sb, struct smb_fattr *fattr)
{
struct smb_sb_info *server = SMB_SB(sb);
struct inode *result;
DEBUG1("smb_iget: %p\n", fattr);
......@@ -126,8 +127,15 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr)
result->i_fop = &smb_file_operations;
result->i_data.a_ops = &smb_file_aops;
} else if (S_ISDIR(result->i_mode)) {
result->i_op = &smb_dir_inode_operations;
if (server->opt.capabilities & SMB_CAP_UNIX)
result->i_op = &smb_dir_inode_operations_unix;
else
result->i_op = &smb_dir_inode_operations;
result->i_fop = &smb_dir_operations;
} else if (S_ISLNK(result->i_mode)) {
result->i_op = &smb_link_inode_operations;
} else {
init_special_inode(result, result->i_mode, fattr->f_rdev);
}
insert_inode_hash(result);
return result;
......@@ -235,7 +243,14 @@ smb_refresh_inode(struct dentry *dentry)
/*
* Check whether the type part of the mode changed,
* and don't update the attributes if it did.
*
* And don't dick with the root inode
*/
if (inode->i_ino == 2)
return error;
if (S_ISLNK(inode->i_mode))
return error; /* VFS will deal with it */
if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
smb_set_inode_attr(inode, &fattr);
} else {
......@@ -665,6 +680,17 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr)
refresh = 1;
}
if (server->opt.capabilities & SMB_CAP_UNIX) {
/* For now we don't want to set the size with setattr_unix */
attr->ia_valid &= ~ATTR_SIZE;
/* FIXME: only call if we actually want to set something? */
error = smb_proc_setattr_unix(dentry, attr, 0, 0);
if (!error)
refresh = 1;
goto out;
}
/*
* Initialize the fattr and check for changed fields.
* Note: CTIME under SMB is creation time rather than
......
......@@ -54,6 +54,7 @@ static struct smb_ops smb_ops_core;
static struct smb_ops smb_ops_os2;
static struct smb_ops smb_ops_win95;
static struct smb_ops smb_ops_winNT;
static struct smb_ops smb_ops_unix;
static void
smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
......@@ -71,6 +72,8 @@ smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry,
static int
smb_proc_setattr_ext(struct smb_sb_info *server,
struct inode *inode, struct smb_fattr *fattr);
int
smb_proc_query_cifsunix(struct smb_sb_info *server);
static void
install_ops(struct smb_ops *dst, struct smb_ops *src);
......@@ -521,15 +524,47 @@ smb_ntutc2unixutc(u64 ntutc)
return (time_t)t;
}
#if 0
/* Convert the Unix UTC into NT time */
static u64
smb_unixutc2ntutc(struct smb_sb_info *server, time_t t)
smb_unixutc2ntutc(time_t t)
{
/* Note: timezone conversion is probably wrong. */
return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET;
/* return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET; */
return ((u64)t) * 10000000 + NTFS_TIME_OFFSET;
}
#define MAX_FILE_MODE 6
static mode_t file_mode[] = {
S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK
};
static int smb_filetype_to_mode(u32 filetype)
{
if (filetype > MAX_FILE_MODE) {
PARANOIA("Filetype out of range: %d\n", filetype);
return S_IFREG;
}
return file_mode[filetype];
}
static u32 smb_filetype_from_mode(int mode)
{
if (mode & S_IFREG)
return UNIX_TYPE_FILE;
if (mode & S_IFDIR)
return UNIX_TYPE_DIR;
if (mode & S_IFLNK)
return UNIX_TYPE_SYMLINK;
if (mode & S_IFCHR)
return UNIX_TYPE_CHARDEV;
if (mode & S_IFBLK)
return UNIX_TYPE_BLKDEV;
if (mode & S_IFIFO)
return UNIX_TYPE_FIFO;
if (mode & S_IFSOCK)
return UNIX_TYPE_SOCKET;
return UNIX_TYPE_UNKNOWN;
}
#endif
/*****************************************************************************/
......@@ -913,6 +948,14 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
VERBOSE("Large writes enabled\n");
}
#endif
if (server->opt.capabilities & SMB_CAP_UNIX) {
struct inode *inode;
VERBOSE("Using UNIX CIFS extensions\n");
install_ops(server->ops, &smb_ops_unix);
inode = SB_of(server)->s_root->d_inode;
if (inode)
inode->i_op = &smb_dir_inode_operations_unix;
}
VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n",
server->opt.protocol, server->opt.max_xmit, server->conn_pid,
......@@ -923,6 +966,12 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
server->opt.max_xmit = SMB_MAX_PACKET_SIZE;
}
smb_unlock_server(server);
smbiod_wake_up();
if (server->opt.capabilities & SMB_CAP_UNIX)
smb_proc_query_cifsunix(server);
return error;
out:
smb_unlock_server(server);
smbiod_wake_up();
......@@ -1197,7 +1246,8 @@ smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
* If the file is open with write permissions,
* update the time stamps to sync mtime and atime.
*/
if ((server->opt.protocol >= SMB_PROTOCOL_LANMAN2) &&
if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 &&
(server->opt.protocol >= SMB_PROTOCOL_LANMAN2) &&
!(ei->access == SMB_O_RDONLY))
{
struct smb_fattr fattr;
......@@ -1593,6 +1643,8 @@ smb_set_rw(struct dentry *dentry,struct smb_sb_info *server)
int result;
struct smb_fattr fattr;
/* FIXME: cifsUE should allow removing a readonly file. */
/* first get current attribute */
smb_init_dirent(server, &fattr);
result = server->ops->getattr(server, dentry, &fattr);
......@@ -1762,14 +1814,17 @@ smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
fattr->f_uid = server->mnt->uid;
fattr->f_gid = server->mnt->gid;
fattr->f_blksize = SMB_ST_BLKSIZE;
fattr->f_unix = 0;
}
static void
smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
{
if (fattr->f_unix)
return;
fattr->f_mode = server->mnt->file_mode;
if (fattr->attr & aDIR)
{
if (fattr->attr & aDIR) {
fattr->f_mode = server->mnt->dir_mode;
fattr->f_size = SMB_ST_BLKSIZE;
}
......@@ -2006,6 +2061,43 @@ smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
return result;
}
void smb_decode_unix_basic(struct smb_fattr *fattr, char *p)
{
/* FIXME: verify nls support. all is sent as utf8? */
__u64 devmajor, devminor;
fattr->f_unix = 1;
fattr->f_mode = 0;
/* FIXME: use the uniqueID from the remote instead? */
/* 0 L file size in bytes */
/* 8 L file size on disk in bytes (block count) */
/* 40 L uid */
/* 48 L gid */
/* 56 W file type */
/* 60 L devmajor */
/* 68 L devminor */
/* 76 L unique ID (inode) */
/* 84 L permissions */
/* 92 L link count */
fattr->f_size = LVAL(p, 0);
fattr->f_blocks = LVAL(p, 8);
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);
fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56));
if (S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) {
devmajor = LVAL(p, 60);
devminor = LVAL(p, 68);
fattr->f_rdev = ((devmajor & 0xFF) << 8) | (devminor & 0xFF);
}
fattr->f_mode |= LVAL(p, 84);
}
/*
* Interpret a long filename structure using the specified info level:
* level 1 for anything below NT1 protocol
......@@ -2078,6 +2170,18 @@ smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level,
VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
p, len, len, qname->name);
break;
case SMB_FIND_FILE_UNIX:
result = p + WVAL(p, 0);
qname->name = p + 108;
len = strlen(qname->name);
/* FIXME: should we check the length?? */
p += 8;
smb_decode_unix_basic(fattr, p);
VERBOSE("info SMB_FIND_FILE_UNIX at %p, len=%d, name=%.*s\n",
p, len, len, qname->name);
break;
default:
PARANOIA("Unknown info level %d\n", level);
result = p + WVAL(p, 0);
......@@ -2166,9 +2270,12 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
lock_kernel();
/*
* use info level 1 for older servers that don't do 260
* We always prefer unix style. Use info level 1 for older
* servers that don't do 260.
*/
if (server->opt.protocol < SMB_PROTOCOL_NT1)
if (server->opt.capabilities & SMB_CAP_UNIX)
info_level = SMB_FIND_FILE_UNIX;
else if (server->opt.protocol < SMB_PROTOCOL_NT1)
info_level = 1;
result = -ENOMEM;
......@@ -2309,13 +2416,18 @@ smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
* here that those who do not point to a filename do not need
* this info to continue the listing.
*
* OS/2 needs this and talks infolevel 1
* NetApps want lastname with infolevel 260
* OS/2 needs this and talks infolevel 1.
* NetApps want lastname with infolevel 260.
* win2k want lastname with infolevel 260, and points to
* the record not to the name.
* Samba+CifsUnixExt doesn't need lastname.
*
* Both are happy if we return the data they point to. So we do.
* (FIXME: above is not true with win2k)
*/
mask_len = 0;
if (ff_lastname > 0 && ff_lastname < req->rq_ldata) {
if (info_level != SMB_FIND_FILE_UNIX &&
ff_lastname > 0 && ff_lastname < req->rq_ldata) {
lastname = req->rq_data + ff_lastname;
switch (info_level) {
......@@ -2606,6 +2718,30 @@ smb_proc_getattr_trans2_all(struct smb_sb_info *server, struct dentry *dir,
return result;
}
static int
smb_proc_getattr_unix(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *attr)
{
struct smb_request *req;
int result;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
result = smb_proc_getattr_trans2(server, dir, req,
SMB_QUERY_FILE_UNIX_BASIC);
if (result < 0)
goto out_free;
smb_decode_unix_basic(attr, req->rq_data);
out_free:
smb_rput(req);
out:
return result;
}
static int
smb_proc_getattr_95(struct smb_sb_info *server, struct dentry *dir,
struct smb_fattr *attr)
......@@ -2824,6 +2960,120 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
return result;
}
/*
* ATTR_MODE 0x001
* ATTR_UID 0x002
* ATTR_GID 0x004
* ATTR_SIZE 0x008
* ATTR_ATIME 0x010
* ATTR_MTIME 0x020
* ATTR_CTIME 0x040
* ATTR_ATIME_SET 0x080
* ATTR_MTIME_SET 0x100
* ATTR_FORCE 0x200
* ATTR_ATTR_FLAG 0x400
*
* major/minor should only be set by mknod.
*/
int
smb_proc_setattr_unix(struct dentry *d, struct iattr *attr,
int major, int minor)
{
struct smb_sb_info *server = server_from_dentry(d);
u64 nttime;
char *p, *param;
int result;
char data[100];
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
param = req->rq_buffer;
DEBUG1("valid flags = 0x%04x\n", attr->ia_valid);
WSET(param, 0, SMB_SET_FILE_UNIX_BASIC);
DSET(param, 2, 0);
result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL);
if (result < 0)
goto out_free;
p = param + 6 + result;
/* 0 L file size in bytes */
/* 8 L file size on disk in bytes (block count) */
/* 40 L uid */
/* 48 L gid */
/* 56 W file type enum */
/* 60 L devmajor */
/* 68 L devminor */
/* 76 L unique ID (inode) */
/* 84 L permissions */
/* 92 L link count */
LSET(data, 0, SMB_SIZE_NO_CHANGE);
LSET(data, 8, SMB_SIZE_NO_CHANGE);
LSET(data, 16, SMB_TIME_NO_CHANGE);
LSET(data, 24, SMB_TIME_NO_CHANGE);
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));
LSET(data, 60, major);
LSET(data, 68, minor);
LSET(data, 76, 0);
LSET(data, 84, SMB_MODE_NO_CHANGE);
LSET(data, 92, 0);
if (attr->ia_valid & ATTR_SIZE) {
LSET(data, 0, attr->ia_size);
LSET(data, 8, 0); /* can't set anyway */
}
/*
* FIXME: check the conversion function it the correct one
*
* we can't set ctime but we might as well pass this to the server
* and let it ignore it.
*/
if (attr->ia_valid & ATTR_CTIME) {
nttime = smb_unixutc2ntutc(attr->ia_ctime);
LSET(data, 16, nttime);
}
if (attr->ia_valid & ATTR_ATIME) {
nttime = smb_unixutc2ntutc(attr->ia_atime);
LSET(data, 24, nttime);
}
if (attr->ia_valid & ATTR_MTIME) {
nttime = smb_unixutc2ntutc(attr->ia_mtime);
LSET(data, 32, nttime);
}
if (attr->ia_valid & ATTR_UID) {
LSET(data, 40, attr->ia_uid);
}
if (attr->ia_valid & ATTR_GID) {
LSET(data, 48, attr->ia_gid);
}
if (attr->ia_valid & ATTR_MODE) {
LSET(data, 84, attr->ia_mode);
}
req->rq_trans2_command = TRANSACT2_SETPATHINFO;
req->rq_ldata = 100;
req->rq_data = data;
req->rq_lparm = p - param;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
out_free:
smb_rput(req);
out:
return result;
}
/*
* Set the modify and access timestamps for a file.
*
......@@ -2907,6 +3157,189 @@ smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
return result;
}
int
smb_proc_read_link(struct smb_sb_info *server, struct dentry *d,
char *buffer, int len)
{
char *p, *param;
int result;
struct smb_request *req;
DEBUG1("readlink of %s/%s\n", DENTRY_PATH(d));
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
param = req->rq_buffer;
WSET(param, 0, SMB_QUERY_FILE_UNIX_LINK);
DSET(param, 2, 0);
result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, d, NULL);
if (result < 0)
goto out_free;
p = param + 6 + result;
req->rq_trans2_command = TRANSACT2_QPATHINFO;
req->rq_ldata = 0;
req->rq_data = NULL;
req->rq_lparm = p - param;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0)
goto out_free;
DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
&param[6], result, server->rcls, server->err);
/* copy data up to the \0 or buffer length */
result = len;
if (req->rq_ldata < len)
result = req->rq_ldata;
strncpy(buffer, req->rq_data, result);
out_free:
smb_rput(req);
out:
return result;
}
/*
* Create a symlink object called dentry which points to oldpath.
* Samba does not permit dangling links but returns a suitable error message.
*/
int
smb_proc_symlink(struct smb_sb_info *server, struct dentry *d,
const char *oldpath)
{
char *p, *param;
int result;
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
param = req->rq_buffer;
WSET(param, 0, SMB_SET_FILE_UNIX_LINK);
DSET(param, 2, 0);
result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, d, NULL);
if (result < 0)
goto out_free;
p = param + 6 + result;
req->rq_trans2_command = TRANSACT2_SETPATHINFO;
req->rq_ldata = strlen(oldpath) + 1;
req->rq_data = (char *) oldpath;
req->rq_lparm = p - param;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0)
goto out_free;
DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
&param[6], result, server->rcls, server->err);
result = 0;
out_free:
smb_rput(req);
out:
return result;
}
/*
* Create a hard link object called new_dentry which points to dentry.
*/
int
smb_proc_link(struct smb_sb_info *server, struct dentry *dentry,
struct dentry *new_dentry)
{
char *p, *param;
int result;
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, PAGE_SIZE)))
goto out;
param = req->rq_buffer;
WSET(param, 0, SMB_SET_FILE_UNIX_HLINK);
DSET(param, 2, 0);
result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1,
new_dentry, NULL);
if (result < 0)
goto out_free;
p = param + 6 + result;
/* Grr, pointless separation of parameters and data ... */
req->rq_data = p;
req->rq_ldata = smb_encode_path(server, p, SMB_MAXPATHLEN+1,
dentry, NULL);
req->rq_trans2_command = TRANSACT2_SETPATHINFO;
req->rq_lparm = p - param;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0)
goto out_free;
DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
&param[6], result, server->rcls, server->err);
result = 0;
out_free:
smb_rput(req);
out:
return result;
}
int
smb_proc_query_cifsunix(struct smb_sb_info *server)
{
int result;
int major, minor;
u64 caps;
char param[2];
struct smb_request *req;
result = -ENOMEM;
if (! (req = smb_alloc_request(server, 100)))
goto out;
WSET(param, 0, SMB_QUERY_CIFS_UNIX_INFO);
req->rq_trans2_command = TRANSACT2_QFSINFO;
req->rq_ldata = 0;
req->rq_data = NULL;
req->rq_lparm = 2;
req->rq_parm = param;
req->rq_flags = 0;
result = smb_add_request(req);
if (result < 0)
goto out_free;
if (req->rq_ldata < 12) {
PARANOIA("Not enough data\n");
goto out_free;
}
major = WVAL(req->rq_data, 0);
minor = WVAL(req->rq_data, 2);
DEBUG1("Server implements CIFS Extensions for UNIX systems v%d.%d\n",
major, minor);
/* FIXME: verify that we are ok with this major/minor? */
caps = LVAL(req->rq_data, 4);
DEBUG1("Server capabilities 0x%016llx\n", caps);
out_free:
smb_rput(req);
out:
return result;
}
static void
install_ops(struct smb_ops *dst, struct smb_ops *src)
......@@ -2953,3 +3386,15 @@ static struct smb_ops smb_ops_winNT =
.getattr = smb_proc_getattr_trans2_all,
.truncate = smb_proc_trunc64,
};
/* Samba w/ unix extensions. Others? */
static struct smb_ops smb_ops_unix =
{
.read = smb_proc_readX,
.write = smb_proc_writeX,
.readdir = smb_proc_readdir_long,
.getattr = smb_proc_getattr_unix,
/* FIXME: core/ext/time setattr needs to be cleaned up! */
/* .setattr = smb_proc_setattr_unix, */
.truncate = smb_proc_trunc64,
};
/*
* Autogenerated with cproto on: Fri Jul 12 22:15:26 CEST 2002
* Autogenerated with cproto on: Sun Sep 29 21:48:59 CEST 2002
*/
struct smb_request;
......@@ -22,13 +22,20 @@ extern int smb_proc_rmdir(struct dentry *dentry);
extern int smb_proc_unlink(struct dentry *dentry);
extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid);
extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
extern void smb_decode_unix_basic(struct smb_fattr *fattr, char *p);
extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr);
extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr);
extern int smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, int major, int minor);
extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr);
extern int smb_proc_dskattr(struct super_block *sb, struct statfs *attr);
extern int smb_proc_read_link(struct smb_sb_info *server, struct dentry *d, char *buffer, int len);
extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, const char *oldpath);
extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry);
extern int smb_proc_query_cifsunix(struct smb_sb_info *server);
/* dir.c */
extern struct file_operations smb_dir_operations;
extern struct inode_operations smb_dir_inode_operations;
extern struct inode_operations smb_dir_inode_operations_unix;
extern void smb_new_dentry(struct dentry *dentry);
extern void smb_renew_times(struct dentry *dentry);
/* cache.c */
......@@ -76,3 +83,8 @@ extern int smb_add_request(struct smb_request *req);
extern int smb_request_send_req(struct smb_request *req);
extern int smb_request_send_server(struct smb_sb_info *server);
extern int smb_request_recv(struct smb_sb_info *server);
/* symlink.c */
extern int smb_read_link(struct dentry *dentry, char *buffer, int len);
extern int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname);
extern int smb_follow_link(struct dentry *dentry, struct nameidata *nd);
extern struct inode_operations smb_link_inode_operations;
/*
* symlink.c
*
* Copyright (C) 2002 by John Newbigin
*
* Please add a note about your changes to smbfs in the ChangeLog file.
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/smbno.h>
#include <linux/smb_fs.h>
#include "smb_debug.h"
#include "proto.h"
int smb_read_link(struct dentry *dentry, char *buffer, int len)
{
char link[256]; /* FIXME: pain ... */
int r;
DEBUG1("read link buffer len = %d\n", len);
r = smb_proc_read_link(server_from_dentry(dentry), dentry, link,
sizeof(link) - 1);
if (r < 0)
return -ENOENT;
return vfs_readlink(dentry, buffer, len, link);
}
int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname)
{
DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry));
return smb_proc_symlink(server_from_dentry(dentry), dentry, oldname);
}
int smb_follow_link(struct dentry *dentry, struct nameidata *nd)
{
char link[256]; /* FIXME: pain ... */
int len;
DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry));
len = smb_proc_read_link(server_from_dentry(dentry), dentry, link,
sizeof(link) - 1);
if(len < 0)
return -ENOENT;
link[len] = 0;
return vfs_follow_link(nd, link);
}
struct inode_operations smb_link_inode_operations =
{
.readlink smb_read_link,
.follow_link smb_follow_link,
};
......@@ -76,7 +76,6 @@ struct smb_nls_codepage {
* Contains all relevant data on a SMB networked file.
*/
struct smb_fattr {
__u16 attr;
unsigned long f_ino;
......@@ -84,12 +83,14 @@ struct smb_fattr {
nlink_t f_nlink;
uid_t f_uid;
gid_t f_gid;
dev_t f_rdev;
loff_t f_size;
time_t f_atime;
time_t f_mtime;
time_t f_ctime;
unsigned long f_blksize;
unsigned long f_blocks;
int f_unix;
};
enum smb_conn_state {
......
......@@ -112,19 +112,20 @@ smb_kfree(void *obj)
/* NT1 protocol capability bits */
#define SMB_CAP_RAW_MODE 0x0001
#define SMB_CAP_MPX_MODE 0x0002
#define SMB_CAP_UNICODE 0x0004
#define SMB_CAP_LARGE_FILES 0x0008
#define SMB_CAP_NT_SMBS 0x0010
#define SMB_CAP_RPC_REMOTE_APIS 0x0020
#define SMB_CAP_STATUS32 0x0040
#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080
#define SMB_CAP_LOCK_AND_READ 0x0100
#define SMB_CAP_NT_FIND 0x0200
#define SMB_CAP_DFS 0x1000
#define SMB_CAP_LARGE_READX 0x4000
#define SMB_CAP_LARGE_WRITEX 0x8000
#define SMB_CAP_RAW_MODE 0x00000001
#define SMB_CAP_MPX_MODE 0x00000002
#define SMB_CAP_UNICODE 0x00000004
#define SMB_CAP_LARGE_FILES 0x00000008
#define SMB_CAP_NT_SMBS 0x00000010
#define SMB_CAP_RPC_REMOTE_APIS 0x00000020
#define SMB_CAP_STATUS32 0x00000040
#define SMB_CAP_LEVEL_II_OPLOCKS 0x00000080
#define SMB_CAP_LOCK_AND_READ 0x00000100
#define SMB_CAP_NT_FIND 0x00000200
#define SMB_CAP_DFS 0x00001000
#define SMB_CAP_LARGE_READX 0x00004000
#define SMB_CAP_LARGE_WRITEX 0x00008000
#define SMB_CAP_UNIX 0x00800000 /* unofficial ... */
/*
......
......@@ -328,4 +328,36 @@
#define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000
#define SMB_FLAGS2_UNICODE_STRINGS 0x8000
/*
* UNIX stuff (from samba trans2.h)
*/
#define MIN_UNIX_INFO_LEVEL 0x200
#define MAX_UNIX_INFO_LEVEL 0x2FF
#define SMB_FIND_FILE_UNIX 0x202
#define SMB_QUERY_FILE_UNIX_BASIC 0x200
#define SMB_QUERY_FILE_UNIX_LINK 0x201
#define SMB_QUERY_FILE_UNIX_HLINK 0x202
#define SMB_SET_FILE_UNIX_BASIC 0x200
#define SMB_SET_FILE_UNIX_LINK 0x201
#define SMB_SET_FILE_UNIX_HLINK 0x203
#define SMB_QUERY_CIFS_UNIX_INFO 0x200
/* values which means "don't change it" */
#define SMB_MODE_NO_CHANGE 0xFFFFFFFF
#define SMB_UID_NO_CHANGE 0xFFFFFFFF
#define SMB_GID_NO_CHANGE 0xFFFFFFFF
#define SMB_TIME_NO_CHANGE 0xFFFFFFFFFFFFFFFF
#define SMB_SIZE_NO_CHANGE 0xFFFFFFFFFFFFFFFF
/* UNIX filetype mappings. */
#define UNIX_TYPE_FILE 0
#define UNIX_TYPE_DIR 1
#define UNIX_TYPE_SYMLINK 2
#define UNIX_TYPE_CHARDEV 3
#define UNIX_TYPE_BLKDEV 4
#define UNIX_TYPE_FIFO 5
#define UNIX_TYPE_SOCKET 6
#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF
#endif /* _SMBNO_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment