Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54161df1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: (29 commits)
  cifs: fsc should not default to "on"
  [CIFS] remove redundant path walking in dfs_do_refmount
  cifs: ignore the "mand", "nomand" and "_netdev" mount options
  cifs: map NT_STATUS_ERROR_WRITE_PROTECTED to -EROFS
  cifs: don't allow cifs_iget to match inodes of the wrong type
  [CIFS] relinquish fscache cookie before freeing CIFSTconInfo
  cifs: add separate cred_uid field to sesInfo
  fs: cifs: check kmalloc() result
  [CIFS] Missing ifdef
  [CIFS] Missing line from previous commit
  [CIFS] Fix build break when CONFIG_CIFS_FSCACHE disabled
  cifs: add mount option to enable local caching
  cifs: read pages from FS-Cache
  cifs: store pages into local cache
  cifs: FS-Cache page management
  cifs: define inode-level cache object and register them
  cifs: define superblock-level cache index objects and register them
  cifs: remove unused cifsUidInfo struct
  cifs: clean up cifs_find_smb_ses (try #2)
  cifs: match secType when searching for existing tcp session
  ...
parents be82ae02 cb76d5e2
Loading
Loading
Loading
Loading
+9 −0
Original line number Original line Diff line number Diff line
@@ -131,6 +131,15 @@ config CIFS_DFS_UPCALL
	    IP addresses) which is needed for implicit mounts of DFS junction
	    IP addresses) which is needed for implicit mounts of DFS junction
	    points. If unsure, say N.
	    points. If unsure, say N.


config CIFS_FSCACHE
	  bool "Provide CIFS client caching support (EXPERIMENTAL)"
	  depends on EXPERIMENTAL
	  depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
	  help
	    Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
	    to be cached locally on disk through the general filesystem cache
	    manager. If unsure, say N.

config CIFS_EXPERIMENTAL
config CIFS_EXPERIMENTAL
	  bool "CIFS Experimental Features (EXPERIMENTAL)"
	  bool "CIFS Experimental Features (EXPERIMENTAL)"
	  depends on CIFS && EXPERIMENTAL
	  depends on CIFS && EXPERIMENTAL
+2 −0
Original line number Original line Diff line number Diff line
@@ -11,3 +11,5 @@ cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o


cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o

cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o

fs/cifs/cache.c

0 → 100644
+331 −0
Original line number Original line Diff line number Diff line
/*
 *   fs/cifs/cache.c - CIFS filesystem cache index structure definitions
 *
 *   Copyright (c) 2010 Novell, Inc.
 *   Authors(s): Suresh Jayaraman (sjayaraman@suse.de>
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
#include "fscache.h"
#include "cifs_debug.h"

/*
 * CIFS filesystem definition for FS-Cache
 */
struct fscache_netfs cifs_fscache_netfs = {
	.name = "cifs",
	.version = 0,
};

/*
 * Register CIFS for caching with FS-Cache
 */
int cifs_fscache_register(void)
{
	return fscache_register_netfs(&cifs_fscache_netfs);
}

/*
 * Unregister CIFS for caching
 */
void cifs_fscache_unregister(void)
{
	fscache_unregister_netfs(&cifs_fscache_netfs);
}

/*
 * Key layout of CIFS server cache index object
 */
struct cifs_server_key {
	uint16_t	family;		/* address family */
	uint16_t	port;		/* IP port */
	union {
		struct in_addr	ipv4_addr;
		struct in6_addr	ipv6_addr;
	} addr[0];
};

/*
 * Server object keyed by {IPaddress,port,family} tuple
 */
static uint16_t cifs_server_get_key(const void *cookie_netfs_data,
				   void *buffer, uint16_t maxbuf)
{
	const struct TCP_Server_Info *server = cookie_netfs_data;
	const struct sockaddr *sa = (struct sockaddr *) &server->addr.sockAddr;
	struct cifs_server_key *key = buffer;
	uint16_t key_len = sizeof(struct cifs_server_key);

	memset(key, 0, key_len);

	/*
	 * Should not be a problem as sin_family/sin6_family overlays
	 * sa_family field
	 */
	switch (sa->sa_family) {
	case AF_INET:
		key->family = server->addr.sockAddr.sin_family;
		key->port = server->addr.sockAddr.sin_port;
		key->addr[0].ipv4_addr = server->addr.sockAddr.sin_addr;
		key_len += sizeof(key->addr[0].ipv4_addr);
		break;

	case AF_INET6:
		key->family = server->addr.sockAddr6.sin6_family;
		key->port = server->addr.sockAddr6.sin6_port;
		key->addr[0].ipv6_addr = server->addr.sockAddr6.sin6_addr;
		key_len += sizeof(key->addr[0].ipv6_addr);
		break;

	default:
		cERROR(1, "CIFS: Unknown network family '%d'", sa->sa_family);
		key_len = 0;
		break;
	}

	return key_len;
}

/*
 * Server object for FS-Cache
 */
const struct fscache_cookie_def cifs_fscache_server_index_def = {
	.name = "CIFS.server",
	.type = FSCACHE_COOKIE_TYPE_INDEX,
	.get_key = cifs_server_get_key,
};

/*
 * Auxiliary data attached to CIFS superblock within the cache
 */
struct cifs_fscache_super_auxdata {
	u64	resource_id;		/* unique server resource id */
};

static char *extract_sharename(const char *treename)
{
	const char *src;
	char *delim, *dst;
	int len;

	/* skip double chars at the beginning */
	src = treename + 2;

	/* share name is always preceded by '\\' now */
	delim = strchr(src, '\\');
	if (!delim)
		return ERR_PTR(-EINVAL);
	delim++;
	len = strlen(delim);

	/* caller has to free the memory */
	dst = kstrndup(delim, len, GFP_KERNEL);
	if (!dst)
		return ERR_PTR(-ENOMEM);

	return dst;
}

/*
 * Superblock object currently keyed by share name
 */
static uint16_t cifs_super_get_key(const void *cookie_netfs_data, void *buffer,
				   uint16_t maxbuf)
{
	const struct cifsTconInfo *tcon = cookie_netfs_data;
	char *sharename;
	uint16_t len;

	sharename = extract_sharename(tcon->treeName);
	if (IS_ERR(sharename)) {
		cFYI(1, "CIFS: couldn't extract sharename\n");
		sharename = NULL;
		return 0;
	}

	len = strlen(sharename);
	if (len > maxbuf)
		return 0;

	memcpy(buffer, sharename, len);

	kfree(sharename);

	return len;
}

static uint16_t
cifs_fscache_super_get_aux(const void *cookie_netfs_data, void *buffer,
			   uint16_t maxbuf)
{
	struct cifs_fscache_super_auxdata auxdata;
	const struct cifsTconInfo *tcon = cookie_netfs_data;

	memset(&auxdata, 0, sizeof(auxdata));
	auxdata.resource_id = tcon->resource_id;

	if (maxbuf > sizeof(auxdata))
		maxbuf = sizeof(auxdata);

	memcpy(buffer, &auxdata, maxbuf);

	return maxbuf;
}

static enum
fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data,
					      const void *data,
					      uint16_t datalen)
{
	struct cifs_fscache_super_auxdata auxdata;
	const struct cifsTconInfo *tcon = cookie_netfs_data;

	if (datalen != sizeof(auxdata))
		return FSCACHE_CHECKAUX_OBSOLETE;

	memset(&auxdata, 0, sizeof(auxdata));
	auxdata.resource_id = tcon->resource_id;

	if (memcmp(data, &auxdata, datalen) != 0)
		return FSCACHE_CHECKAUX_OBSOLETE;

	return FSCACHE_CHECKAUX_OKAY;
}

/*
 * Superblock object for FS-Cache
 */
const struct fscache_cookie_def cifs_fscache_super_index_def = {
	.name = "CIFS.super",
	.type = FSCACHE_COOKIE_TYPE_INDEX,
	.get_key = cifs_super_get_key,
	.get_aux = cifs_fscache_super_get_aux,
	.check_aux = cifs_fscache_super_check_aux,
};

/*
 * Auxiliary data attached to CIFS inode within the cache
 */
struct cifs_fscache_inode_auxdata {
	struct timespec	last_write_time;
	struct timespec	last_change_time;
	u64		eof;
};

static uint16_t cifs_fscache_inode_get_key(const void *cookie_netfs_data,
					   void *buffer, uint16_t maxbuf)
{
	const struct cifsInodeInfo *cifsi = cookie_netfs_data;
	uint16_t keylen;

	/* use the UniqueId as the key */
	keylen = sizeof(cifsi->uniqueid);
	if (keylen > maxbuf)
		keylen = 0;
	else
		memcpy(buffer, &cifsi->uniqueid, keylen);

	return keylen;
}

static void
cifs_fscache_inode_get_attr(const void *cookie_netfs_data, uint64_t *size)
{
	const struct cifsInodeInfo *cifsi = cookie_netfs_data;

	*size = cifsi->vfs_inode.i_size;
}

static uint16_t
cifs_fscache_inode_get_aux(const void *cookie_netfs_data, void *buffer,
			   uint16_t maxbuf)
{
	struct cifs_fscache_inode_auxdata auxdata;
	const struct cifsInodeInfo *cifsi = cookie_netfs_data;

	memset(&auxdata, 0, sizeof(auxdata));
	auxdata.eof = cifsi->server_eof;
	auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
	auxdata.last_change_time = cifsi->vfs_inode.i_ctime;

	if (maxbuf > sizeof(auxdata))
		maxbuf = sizeof(auxdata);

	memcpy(buffer, &auxdata, maxbuf);

	return maxbuf;
}

static enum
fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data,
					      const void *data,
					      uint16_t datalen)
{
	struct cifs_fscache_inode_auxdata auxdata;
	struct cifsInodeInfo *cifsi = cookie_netfs_data;

	if (datalen != sizeof(auxdata))
		return FSCACHE_CHECKAUX_OBSOLETE;

	memset(&auxdata, 0, sizeof(auxdata));
	auxdata.eof = cifsi->server_eof;
	auxdata.last_write_time = cifsi->vfs_inode.i_mtime;
	auxdata.last_change_time = cifsi->vfs_inode.i_ctime;

	if (memcmp(data, &auxdata, datalen) != 0)
		return FSCACHE_CHECKAUX_OBSOLETE;

	return FSCACHE_CHECKAUX_OKAY;
}

static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data)
{
	struct cifsInodeInfo *cifsi = cookie_netfs_data;
	struct pagevec pvec;
	pgoff_t first;
	int loop, nr_pages;

	pagevec_init(&pvec, 0);
	first = 0;

	cFYI(1, "cifs inode 0x%p now uncached", cifsi);

	for (;;) {
		nr_pages = pagevec_lookup(&pvec,
					  cifsi->vfs_inode.i_mapping, first,
					  PAGEVEC_SIZE - pagevec_count(&pvec));
		if (!nr_pages)
			break;

		for (loop = 0; loop < nr_pages; loop++)
			ClearPageFsCache(pvec.pages[loop]);

		first = pvec.pages[nr_pages - 1]->index + 1;

		pvec.nr = nr_pages;
		pagevec_release(&pvec);
		cond_resched();
	}
}

const struct fscache_cookie_def cifs_fscache_inode_object_def = {
	.name		= "CIFS.uniqueid",
	.type		= FSCACHE_COOKIE_TYPE_DATAFILE,
	.get_key	= cifs_fscache_inode_get_key,
	.get_attr	= cifs_fscache_inode_get_attr,
	.get_aux	= cifs_fscache_inode_get_aux,
	.check_aux	= cifs_fscache_inode_check_aux,
	.now_uncached	= cifs_fscache_inode_now_uncached,
};
+11 −17
Original line number Original line Diff line number Diff line
@@ -230,28 +230,22 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
	goto compose_mount_options_out;
	goto compose_mount_options_out;
}
}



/**
static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent,
 * cifs_dfs_do_refmount - mounts specified path using provided refferal
		struct dentry *dentry, const struct dfs_info3_param *ref)
 * @cifs_sb:		parent/root superblock
 * @fullpath:		full path in UNC format
 * @ref:		server's referral
 */
static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb,
		const char *fullpath, const struct dfs_info3_param *ref)
{
{
	struct cifs_sb_info *cifs_sb;
	struct vfsmount *mnt;
	struct vfsmount *mnt;
	char *mountdata;
	char *mountdata;
	char *devname = NULL;
	char *devname = NULL;
	char *fullpath;

	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
	/*
	 * this function gives us a path with a double backslash prefix. We
	 * require a single backslash for DFS.
	 */
	fullpath = build_path_from_dentry(dentry);
	if (!fullpath)
		return ERR_PTR(-ENOMEM);


	/* strip first '\' from fullpath */
	mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
	mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
			fullpath + 1, ref, &devname);
			fullpath + 1, ref, &devname);
	kfree(fullpath);


	if (IS_ERR(mountdata))
	if (IS_ERR(mountdata))
		return (struct vfsmount *)mountdata;
		return (struct vfsmount *)mountdata;
@@ -357,8 +351,8 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
			rc = -EINVAL;
			rc = -EINVAL;
			goto out_err;
			goto out_err;
		}
		}
		mnt = cifs_dfs_do_refmount(nd->path.mnt,
		mnt = cifs_dfs_do_refmount(cifs_sb,
				nd->path.dentry, referrals + i);
				full_path, referrals + i);
		cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
		cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__,
					referrals[i].node_name, mnt);
					referrals[i].node_name, mnt);


+1 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@
#define CIFS_MOUNT_DYNPERM      0x1000 /* allow in-memory only mode setting   */
#define CIFS_MOUNT_DYNPERM      0x1000 /* allow in-memory only mode setting   */
#define CIFS_MOUNT_NOPOSIXBRL   0x2000 /* mandatory not posix byte range lock */
#define CIFS_MOUNT_NOPOSIXBRL   0x2000 /* mandatory not posix byte range lock */
#define CIFS_MOUNT_NOSSYNC      0x4000 /* don't do slow SMBflush on every sync*/
#define CIFS_MOUNT_NOSSYNC      0x4000 /* don't do slow SMBflush on every sync*/
#define CIFS_MOUNT_FSCACHE	0x8000 /* local caching enabled */


struct cifs_sb_info {
struct cifs_sb_info {
	struct cifsTconInfo *tcon;	/* primary mount */
	struct cifsTconInfo *tcon;	/* primary mount */
Loading