cifs_dfs_ref.c

来自「Linux内核自带的cifs模块」· C语言 代码 · 共 451 行

C
451
字号
/* *  fs/cifs/cifs_dfs_ref.c * *   Copyright (C) International Business Machines  Corp., 2007 *   Author(s): Steve French (sfrench@us.ibm.com) *              Igor Mammedov (niallain@gmail.com) *   Contains the CIFS DFS upcall routines * *   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 <linux/dcache.h>#include <linux/mount.h>#include <linux/namei.h>#include <linux/vfs.h>#include <linux/fs.h>#include "cifsfs.h"#include "cifspdu.h"#include "cifsglob.h"#include "cifsproto.h"#include "cifs_debug.h"#include "cifs_fs_sb.h"#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)#ifdef CONFIG_CIFS_EXPERIMENTALLIST_HEAD(cifs_dfs_automount_list);static void cifs_resolver_describe(const struct key *key, struct seq_file *m){	cFYI(1, ("%s: key->description: %s", __FUNCTION__,  key->description ));	seq_puts(m, key->description);	seq_printf(m, ": %u", key->datalen);}static int cifs_resolver_match(const struct key *key, const void *description){	cFYI(1, ("%s: key->description: %s ; desc: %s ", __FUNCTION__,		key->description, (char *)description ));	return strcmp(key->description, description) == 0;}static int cifs_resolver_instantiate(struct key *key, const void *data,				     size_t datalen){	char *ip = NULL;	int rc;	struct in_addr ip_addr;	/* convert dot ip addr to numeric form */	/* BB Do we have to pass datalen? Are we guaranteed that the key	 * is null terminated? */	rc = cifs_inet_pton(AF_INET, (char *)data, &(ip_addr.s_addr));	if (rc <= 0) {		ip = kmalloc(datalen+1, GFP_KERNEL);		if (ip == NULL) {			return -ENOMEM;		}		strncpy( ip, (char *)data, datalen);		ip[datalen] = 0;		cFYI(1, ("%s: failed to convert ip to binary: %s",			__FUNCTION__, ip));		kfree(ip);		rc = -EINVAL;		return rc;	}	rc = 0;	key->payload.value = ip_addr.s_addr;	return rc;}struct key_type key_type_cifs_resolver ={	.name        = "cifs_resolver",	.def_datalen = sizeof(struct in_addr),	.describe    = cifs_resolver_describe,	.instantiate = cifs_resolver_instantiate,	.match       = cifs_resolver_match,};intcifs_resolve_server_name_to_ip(const char *unc, struct in_addr *ip_addr) {	int rc = -EAGAIN;	struct key *rkey;	char *name;	int len;	if ((!ip_addr) || (!unc)) {		return -EINVAL;	}	/* search for server name delimiter */	len = strlen( unc);	if (len < 3) {		cFYI(1, ("%s: unc is too short: %s",			 __FUNCTION__, unc ));		return -EINVAL;	}	len -= 2;	name = memchr(unc+2, '\\', len);	if (!name) {		cFYI(1, ("%s: probably server name is whole unc: %s",			 __FUNCTION__, unc ));	} else {		len = (name - unc) - 2/* leading // */;	}	name = kmalloc( len+1, GFP_KERNEL);	if (name == NULL) {		rc = -ENOMEM;		return rc;	}	memcpy( name, unc+2, len);	name[len] = 0;	rkey = request_key(&key_type_cifs_resolver, name, "");	if (!IS_ERR(rkey)) {		ip_addr->s_addr = rkey->payload.value;		cFYI(1, ("%s: resolved: %s to %u.%u.%u.%u", __FUNCTION__,					rkey->description,					NIPQUAD(ip_addr->s_addr)			));		key_put(rkey);		rc = 0;	} else {		cERROR(1, ("%s: unable to resolve: %s", __FUNCTION__, name));	}	kfree(name);	return rc;}char *cifs_get_share_name(const char *node_name){	int len;	char *UNC;	char *pSep;	len = strlen(node_name);	UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */,			 GFP_KERNEL);	if (!UNC) {		return NULL;	}	/* get share name and server name */	if (node_name[1] != '\\'){		UNC[0] = '\\';		strncpy(UNC+1, node_name, len);		len++;		UNC[len] = 0;	} else {		strncpy(UNC, node_name, len);		UNC[len] = 0;	}	/* find server name end */	pSep = memchr(UNC+2, '\\', len-2);	if (!pSep) {		cERROR(1, ("%s: no server name end in node name: %s",			__FUNCTION__, node_name ));		kfree(UNC);		return NULL;	}	/* find sharename end */	pSep++;	pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC));	if (!pSep) {		cERROR(1, ("%s:2 cant find share name in node name: %s",			__FUNCTION__, node_name ));		kfree(UNC);		return NULL;	}	/* trim path up to sharename end	 *          * now we have share name in UNC */	*pSep = 0;	len = pSep-UNC;	return UNC;}struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent,				      struct dentry *dentry, char *ref_unc){	int rc;	struct cifs_sb_info *cifs_sb;	struct sockaddr_in sin_server;	struct vfsmount *mnt = ERR_PTR(-ENOENT);	char *mountdata;	int md_len;	char *devname;	char *tkn_e;	char srvIP[16];	char sep = ',';	int off, noff;	cFYI(1, ("in %s", __FUNCTION__ ));	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);	if ( cifs_sb->mountdata == NULL ) {		return ERR_PTR(-EINVAL);	}	devname = cifs_get_share_name(ref_unc);	rc = cifs_resolve_server_name_to_ip( devname, &(sin_server.sin_addr));	snprintf(srvIP, sizeof(srvIP), "%u.%u.%u.%u",			NIPQUAD(sin_server.sin_addr));	if (rc != 0) {		if (devname) kfree(devname);		cERROR(1, ("%s: failed to resolve server part of %s to IP",			  __FUNCTION__, devname ));		rc = -EINVAL;		return ERR_PTR(rc);	}	srvIP[sizeof(srvIP)-1] = '\0';	md_len = strlen(cifs_sb->mountdata) + sizeof(srvIP) +			strlen(ref_unc) + 3;	mountdata = kzalloc(md_len+1, GFP_KERNEL);	/* copy all options except unc,ip,prefixpath */	off = 0;	if (strncmp(cifs_sb->mountdata, "sep=", 4) == 0) {			sep = cifs_sb->mountdata[4];			strncpy(mountdata, cifs_sb->mountdata, 5);			off += 5;	}	while ( (tkn_e = strchr(cifs_sb->mountdata+off, sep)) ) {		noff = (tkn_e - (cifs_sb->mountdata+off)) + 1;		if (strnicmp(cifs_sb->mountdata+off, "unc=", 4) == 0) {			off += noff;			continue;		}		if (strnicmp(cifs_sb->mountdata+off, "ip=", 3) == 0) {			off += noff;			continue;		}		if (strnicmp(cifs_sb->mountdata+off, "prefixpath=", 3) == 0) {			off += noff;			continue;		}		strncat(mountdata, cifs_sb->mountdata+off, noff);		off += noff;	}	strcat(mountdata, cifs_sb->mountdata+off);	mountdata[md_len] = '\0';	strcat(mountdata, ", ip="); strcat(mountdata, srvIP);	strcat(mountdata, ", unc="); strcat(mountdata, devname);	/* find prefixpath */	tkn_e = strchr(ref_unc+2, '\\');	if ( tkn_e ) {		tkn_e = strchr(tkn_e+1, '\\');		if ( tkn_e ) {			strcat(mountdata, ",prefixpath=");			strcat(mountdata, tkn_e);		}	}	/*cFYI(1,("%s: old mountdata: %s", __FUNCTION__,cifs_sb->mountdata));*/	/*cFYI(1, ("%s: new mountdata: %s", __FUNCTION__, mountdata ));*/	mnt = vfs_kern_mount(&cifs_fs_type, 0, devname, mountdata);	if (devname)		kfree(devname);	if (mountdata)		kfree(mountdata);	cFYI(1, ("leaving %s", __FUNCTION__ ));	return mnt;}static char *build_full_dfs_path_from_dentry(struct dentry *dentry){	char *full_path = NULL;	char *search_path;	char *tmp_path;	size_t l_max_len;	struct cifs_sb_info *cifs_sb;	if ( dentry->d_inode == NULL ) {		return NULL;	}	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);	if ( cifs_sb->tcon == NULL ) {		return NULL;	}	search_path = build_path_from_dentry(dentry);	if (search_path == NULL) {		return NULL;	}	if (cifs_sb->tcon->Flags & 0x2) {		/* we should use full path name to correct working with DFS */		l_max_len = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1)				     + strnlen(search_path, MAX_PATHCONF) + 1;		tmp_path = kmalloc(l_max_len, GFP_KERNEL);		if (tmp_path == NULL) {			kfree(search_path);			return NULL;		}		strncpy(tmp_path, cifs_sb->tcon->treeName, l_max_len);		strcat(tmp_path, search_path);		tmp_path[l_max_len-1] = 0;		full_path = tmp_path;		kfree(search_path);	} else {		full_path = search_path;	}	return full_path;}static void *cifs_dfs_follow_mountpoint(struct dentry *dentry,					struct nameidata *nd){	DFS_INFO3_PARAM *referrals = NULL;	unsigned int num_referrals = 0;	struct cifs_sb_info *cifs_sb;	struct cifsSesInfo *ses;	char *full_path = NULL;	int xid, i;	int rc = 0;	struct vfsmount *mnt = ERR_PTR(-ENOENT);	cFYI(1, ("in %s", __FUNCTION__ ));	BUG_ON(IS_ROOT(dentry));	xid = GetXid();	dput(nd->dentry);	nd->dentry = dget(dentry);	if (d_mountpoint(nd->dentry)) {		goto out_follow;	}	if ( dentry->d_inode == NULL ) {		rc = -EINVAL;		goto out_err;	}	cifs_sb = CIFS_SB(dentry->d_inode->i_sb);	ses = cifs_sb->tcon->ses;	if ( !ses ) {		rc = -EINVAL;		goto out_err;	}	full_path = build_full_dfs_path_from_dentry(dentry);	if ( full_path == NULL ) {		rc = -ENOMEM;		goto out_err;	}	rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls,			&num_referrals, &referrals,			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);	for (i = 0; i < num_referrals; i++) {		cFYI(1, ("%s: ref path: %s", __FUNCTION__,			 referrals[i].path_name));		cFYI(1, ("%s: node path: %s", __FUNCTION__,			 referrals[i].node_name ));		cFYI(1, ("%s: fl: %hd, serv_type: %hd, ref_flags: %hd, "			 "path_consumed: %hd", __FUNCTION__,			 referrals[i].flags, referrals[i].server_type,			 referrals[i].ref_flag, referrals[i].PathConsumed));		/* connect to storage node */		if (referrals[i].flags & DFSREF_STORAGE_SERVER) {			int len;			len = strlen(referrals[i].node_name);			if (len < 2) {				cERROR(1, ("%s: Net Address path too short: %s",					__FUNCTION__, referrals[i].node_name ));				rc = -EINVAL;				goto out_err;			} else {				mnt = cifs_dfs_do_refmount(nd->mnt,						nd->dentry,						referrals[i].node_name);				cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p",					 __FUNCTION__,					 referrals[i].node_name, mnt));				if ( !rc ) {					/* have server so stop here & return */					break;				}			}		}	}	rc = PTR_ERR(mnt);	if (IS_ERR(mnt))		goto out_err;	mntget(mnt);	rc = do_add_mount(mnt, nd, nd->mnt->mnt_flags,			  &cifs_dfs_automount_list);	if (rc < 0) {		mntput(mnt);		if (rc == -EBUSY)			goto out_follow;		goto out_err;	}	mntput(nd->mnt);	dput(nd->dentry);	nd->mnt = mnt;	nd->dentry = dget(mnt->mnt_root);out:	FreeXid(xid);	free_dfs_info_array(referrals, num_referrals);	cFYI(1, ("leaving %s", __FUNCTION__ ));	return ERR_PTR(rc);out_err:	if ( full_path ) kfree(full_path);	path_release(nd);	goto out;out_follow:	while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))		;	rc = 0;	goto out;}struct inode_operations cifs_dfs_referral_inode_operations = {	.follow_link = cifs_dfs_follow_mountpoint,};#endif /* CONFIG_CIFS_EXPERIMENTAL */#endif /* Kernel version 2.6.18 or higher */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?