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. 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> 2001-08-03 Urban Widmark <urban@teststation.com>
* *.c: Unicode support * *.c: Unicode support
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
obj-$(CONFIG_SMB_FS) += smbfs.o obj-$(CONFIG_SMB_FS) += smbfs.o
smbfs-objs := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.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 # If you want debugging output, you may add these flags to the EXTRA_CFLAGS
# SMBFS_PARANOIA should normally be enabled. # SMBFS_PARANOIA should normally be enabled.
...@@ -24,7 +24,8 @@ include $(TOPDIR)/Rules.make ...@@ -24,7 +24,8 @@ include $(TOPDIR)/Rules.make
# #
# getopt.c not included. It is intentionally separate # 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: proto:
-rm -f proto.h -rm -f proto.h
......
...@@ -30,6 +30,8 @@ static int smb_rmdir(struct inode *, struct dentry *); ...@@ -30,6 +30,8 @@ static int smb_rmdir(struct inode *, struct dentry *);
static int smb_unlink(struct inode *, struct dentry *); static int smb_unlink(struct inode *, struct dentry *);
static int smb_rename(struct inode *, struct dentry *, static int smb_rename(struct inode *, struct dentry *,
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 = struct file_operations smb_dir_operations =
{ {
...@@ -51,6 +53,21 @@ struct inode_operations smb_dir_inode_operations = ...@@ -51,6 +53,21 @@ struct inode_operations smb_dir_inode_operations =
.setattr = smb_notify_change, .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. * Read a directory, using filldir to fill the dirent memory.
* smb_proc_readdir does the actual reading from the smb server. * 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) ...@@ -485,8 +502,10 @@ smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id)
static int static int
smb_create(struct inode *dir, struct dentry *dentry, int mode) smb_create(struct inode *dir, struct dentry *dentry, int mode)
{ {
struct smb_sb_info *server = server_from_dentry(dentry);
__u16 fileid; __u16 fileid;
int error; int error;
struct iattr attr;
VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode); 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) ...@@ -494,6 +513,12 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode)
smb_invalid_dir_cache(dir); smb_invalid_dir_cache(dir);
error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid); error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid);
if (!error) { 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); error = smb_instantiate(dentry, fileid, 1);
} else { } else {
PARANOIA("%s/%s failed, error=%d\n", PARANOIA("%s/%s failed, error=%d\n",
...@@ -507,12 +532,20 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode) ...@@ -507,12 +532,20 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode)
static int static int
smb_mkdir(struct inode *dir, struct dentry *dentry, int mode) smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{ {
struct smb_sb_info *server = server_from_dentry(dentry);
int error; int error;
struct iattr attr;
lock_kernel(); lock_kernel();
smb_invalid_dir_cache(dir); smb_invalid_dir_cache(dir);
error = smb_proc_mkdir(dentry); error = smb_proc_mkdir(dentry);
if (!error) { 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); error = smb_instantiate(dentry, 0, 0);
} }
unlock_kernel(); unlock_kernel();
...@@ -601,3 +634,46 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -601,3 +634,46 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry,
unlock_kernel(); unlock_kernel();
return error; 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 = ...@@ -106,6 +106,7 @@ static struct super_operations smb_sops =
struct inode * struct inode *
smb_iget(struct super_block *sb, struct smb_fattr *fattr) smb_iget(struct super_block *sb, struct smb_fattr *fattr)
{ {
struct smb_sb_info *server = SMB_SB(sb);
struct inode *result; struct inode *result;
DEBUG1("smb_iget: %p\n", fattr); DEBUG1("smb_iget: %p\n", fattr);
...@@ -126,8 +127,15 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr) ...@@ -126,8 +127,15 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr)
result->i_fop = &smb_file_operations; result->i_fop = &smb_file_operations;
result->i_data.a_ops = &smb_file_aops; result->i_data.a_ops = &smb_file_aops;
} else if (S_ISDIR(result->i_mode)) { } else if (S_ISDIR(result->i_mode)) {
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_op = &smb_dir_inode_operations;
result->i_fop = &smb_dir_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); insert_inode_hash(result);
return result; return result;
...@@ -235,7 +243,14 @@ smb_refresh_inode(struct dentry *dentry) ...@@ -235,7 +243,14 @@ smb_refresh_inode(struct dentry *dentry)
/* /*
* Check whether the type part of the mode changed, * Check whether the type part of the mode changed,
* and don't update the attributes if it did. * 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)) { if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
smb_set_inode_attr(inode, &fattr); smb_set_inode_attr(inode, &fattr);
} else { } else {
...@@ -665,6 +680,17 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr) ...@@ -665,6 +680,17 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr)
refresh = 1; 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. * Initialize the fattr and check for changed fields.
* Note: CTIME under SMB is creation time rather than * Note: CTIME under SMB is creation time rather than
......
This diff is collapsed.
/* /*
* 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; struct smb_request;
...@@ -22,13 +22,20 @@ extern int smb_proc_rmdir(struct dentry *dentry); ...@@ -22,13 +22,20 @@ extern int smb_proc_rmdir(struct dentry *dentry);
extern int smb_proc_unlink(struct dentry *dentry); extern int smb_proc_unlink(struct dentry *dentry);
extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid); 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_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_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(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_settime(struct dentry *dentry, struct smb_fattr *fattr);
extern int smb_proc_dskattr(struct super_block *sb, struct statfs *attr); 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 */ /* dir.c */
extern struct file_operations smb_dir_operations; extern struct file_operations smb_dir_operations;
extern struct inode_operations smb_dir_inode_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_new_dentry(struct dentry *dentry);
extern void smb_renew_times(struct dentry *dentry); extern void smb_renew_times(struct dentry *dentry);
/* cache.c */ /* cache.c */
...@@ -76,3 +83,8 @@ extern int smb_add_request(struct smb_request *req); ...@@ -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_req(struct smb_request *req);
extern int smb_request_send_server(struct smb_sb_info *server); extern int smb_request_send_server(struct smb_sb_info *server);
extern int smb_request_recv(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 { ...@@ -76,7 +76,6 @@ struct smb_nls_codepage {
* Contains all relevant data on a SMB networked file. * Contains all relevant data on a SMB networked file.
*/ */
struct smb_fattr { struct smb_fattr {
__u16 attr; __u16 attr;
unsigned long f_ino; unsigned long f_ino;
...@@ -84,12 +83,14 @@ struct smb_fattr { ...@@ -84,12 +83,14 @@ struct smb_fattr {
nlink_t f_nlink; nlink_t f_nlink;
uid_t f_uid; uid_t f_uid;
gid_t f_gid; gid_t f_gid;
dev_t f_rdev;
loff_t f_size; loff_t f_size;
time_t f_atime; time_t f_atime;
time_t f_mtime; time_t f_mtime;
time_t f_ctime; time_t f_ctime;
unsigned long f_blksize; unsigned long f_blksize;
unsigned long f_blocks; unsigned long f_blocks;
int f_unix;
}; };
enum smb_conn_state { enum smb_conn_state {
......
...@@ -112,19 +112,20 @@ smb_kfree(void *obj) ...@@ -112,19 +112,20 @@ smb_kfree(void *obj)
/* NT1 protocol capability bits */ /* NT1 protocol capability bits */
#define SMB_CAP_RAW_MODE 0x0001 #define SMB_CAP_RAW_MODE 0x00000001
#define SMB_CAP_MPX_MODE 0x0002 #define SMB_CAP_MPX_MODE 0x00000002
#define SMB_CAP_UNICODE 0x0004 #define SMB_CAP_UNICODE 0x00000004
#define SMB_CAP_LARGE_FILES 0x0008 #define SMB_CAP_LARGE_FILES 0x00000008
#define SMB_CAP_NT_SMBS 0x0010 #define SMB_CAP_NT_SMBS 0x00000010
#define SMB_CAP_RPC_REMOTE_APIS 0x0020 #define SMB_CAP_RPC_REMOTE_APIS 0x00000020
#define SMB_CAP_STATUS32 0x0040 #define SMB_CAP_STATUS32 0x00000040
#define SMB_CAP_LEVEL_II_OPLOCKS 0x0080 #define SMB_CAP_LEVEL_II_OPLOCKS 0x00000080
#define SMB_CAP_LOCK_AND_READ 0x0100 #define SMB_CAP_LOCK_AND_READ 0x00000100
#define SMB_CAP_NT_FIND 0x0200 #define SMB_CAP_NT_FIND 0x00000200
#define SMB_CAP_DFS 0x1000 #define SMB_CAP_DFS 0x00001000
#define SMB_CAP_LARGE_READX 0x4000 #define SMB_CAP_LARGE_READX 0x00004000
#define SMB_CAP_LARGE_WRITEX 0x8000 #define SMB_CAP_LARGE_WRITEX 0x00008000
#define SMB_CAP_UNIX 0x00800000 /* unofficial ... */
/* /*
......
...@@ -328,4 +328,36 @@ ...@@ -328,4 +328,36 @@
#define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000 #define SMB_FLAGS2_32_BIT_ERROR_CODES 0x4000
#define SMB_FLAGS2_UNICODE_STRINGS 0x8000 #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_ */ #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