idmap.c 12.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
/*
 * fs/nfs/idmap.c
 *
 *  UID and GID to name mapping for clients.
 *
 *  Copyright (c) 2002 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Marius Aamodt Eriksen <marius@umich.edu>
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the University nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/sched.h>

#include <linux/sunrpc/clnt.h>
46
#include <linux/workqueue.h>
47 48 49 50 51 52 53 54 55 56
#include <linux/sunrpc/rpc_pipe_fs.h>

#include <linux/nfs_fs_sb.h>
#include <linux/nfs_fs.h>

#include <linux/nfs_idmap.h>

#define IDMAP_HASH_SZ          128

struct idmap_hashent {
57 58 59 60 61 62 63 64
	__u32 ih_id;
	int ih_namelen;
	char ih_name[IDMAP_NAMESZ];
};

struct idmap_hashtable {
	__u8 h_type;
	struct idmap_hashent h_entries[IDMAP_HASH_SZ];
65 66 67 68 69 70 71
};

struct idmap {
	char                  idmap_path[48];
	struct dentry        *idmap_dentry;
	wait_queue_head_t     idmap_wq;
	struct idmap_msg      idmap_im;
72 73 74 75
	struct semaphore      idmap_lock;    /* Serializes upcalls */
	struct semaphore      idmap_im_lock; /* Protects the hashtable */
	struct idmap_hashtable idmap_user_hash;
	struct idmap_hashtable idmap_group_hash;
76 77
};

78 79 80 81
static ssize_t   idmap_pipe_upcall(struct file *, struct rpc_pipe_msg *,
		     char __user *, size_t);
static ssize_t   idmap_pipe_downcall(struct file *, const char __user *,
		     size_t);
82 83
void             idmap_pipe_destroy_msg(struct rpc_pipe_msg *);

84
static unsigned int fnvhash32(const void *, size_t);
85 86 87 88 89 90 91

static struct rpc_pipe_ops idmap_upcall_ops = {
        .upcall         = idmap_pipe_upcall,
        .downcall       = idmap_pipe_downcall,
        .destroy_msg    = idmap_pipe_destroy_msg,
};

92 93
void
nfs_idmap_new(struct nfs4_client *clp)
94 95 96
{
	struct idmap *idmap;

97 98
	if (clp->cl_idmap != NULL)
		return;
99
        if ((idmap = kmalloc(sizeof(*idmap), GFP_KERNEL)) == NULL)
100
                return;
101 102 103 104

	memset(idmap, 0, sizeof(*idmap));

	snprintf(idmap->idmap_path, sizeof(idmap->idmap_path),
105
	    "%s/idmap", clp->cl_rpcclient->cl_pathname);
106 107

        idmap->idmap_dentry = rpc_mkpipe(idmap->idmap_path,
108
	    idmap, &idmap_upcall_ops, 0);
109 110 111 112
        if (IS_ERR(idmap->idmap_dentry)) {
		kfree(idmap);
		return;
	}
113 114 115

        init_MUTEX(&idmap->idmap_lock);
        init_MUTEX(&idmap->idmap_im_lock);
116
	init_waitqueue_head(&idmap->idmap_wq);
117 118
	idmap->idmap_user_hash.h_type = IDMAP_TYPE_USER;
	idmap->idmap_group_hash.h_type = IDMAP_TYPE_GROUP;
119

120
	clp->cl_idmap = idmap;
121 122 123
}

void
124
nfs_idmap_delete(struct nfs4_client *clp)
125
{
126
	struct idmap *idmap = clp->cl_idmap;
127

128 129
	if (!idmap)
		return;
130
	rpc_unlink(idmap->idmap_path);
131
	clp->cl_idmap = NULL;
132 133 134
	kfree(idmap);
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
/*
 * Helper routines for manipulating the hashtable
 */
static inline struct idmap_hashent *
idmap_name_hash(struct idmap_hashtable* h, const char *name, size_t len)
{
	return &h->h_entries[fnvhash32(name, len) % IDMAP_HASH_SZ];
}

static struct idmap_hashent *
idmap_lookup_name(struct idmap_hashtable *h, const char *name, size_t len)
{
	struct idmap_hashent *he = idmap_name_hash(h, name, len);

	if (he->ih_namelen != len || memcmp(he->ih_name, name, len) != 0)
		return NULL;
	return he;
}

static inline struct idmap_hashent *
idmap_id_hash(struct idmap_hashtable* h, __u32 id)
{
	return &h->h_entries[fnvhash32(&id, sizeof(id)) % IDMAP_HASH_SZ];
}

static struct idmap_hashent *
idmap_lookup_id(struct idmap_hashtable *h, __u32 id)
{
	struct idmap_hashent *he = idmap_id_hash(h, id);
	if (he->ih_id != id || he->ih_namelen == 0)
		return NULL;
	return he;
}

/*
 * Routines for allocating new entries in the hashtable.
 * For now, we just have 1 entry per bucket, so it's all
 * pretty trivial.
 */
static inline struct idmap_hashent *
idmap_alloc_name(struct idmap_hashtable *h, char *name, unsigned len)
{
	return idmap_name_hash(h, name, len);
}

static inline struct idmap_hashent *
idmap_alloc_id(struct idmap_hashtable *h, __u32 id)
{
	return idmap_id_hash(h, id);
}

static void
idmap_update_entry(struct idmap_hashent *he, const char *name,
		size_t namelen, __u32 id)
{
	he->ih_id = id;
	memcpy(he->ih_name, name, namelen);
	he->ih_name[namelen] = '\0';
	he->ih_namelen = namelen;
}

196 197 198
/*
 * Name -> ID
 */
199 200 201
static int
nfs_idmap_id(struct idmap *idmap, struct idmap_hashtable *h,
		const char *name, size_t namelen, __u32 *id)
202 203 204
{
	struct rpc_pipe_msg msg;
	struct idmap_msg *im;
205
	struct idmap_hashent *he;
206
	DECLARE_WAITQUEUE(wq, current);
207
	int ret = -EIO;
208 209 210

	im = &idmap->idmap_im;

211 212 213 214 215 216 217 218 219 220 221 222 223
	/*
	 * String sanity checks
	 * Note that the userland daemon expects NUL terminated strings
	 */
	for (;;) {
		if (namelen == 0)
			return -EINVAL;
		if (name[namelen-1] != '\0')
			break;
		namelen--;
	}
	if (namelen >= IDMAP_NAMESZ)
		return -EINVAL;
224 225 226 227

	down(&idmap->idmap_lock);
	down(&idmap->idmap_im_lock);

228 229 230
	he = idmap_lookup_name(h, name, namelen);
	if (he != NULL) {
		*id = he->ih_id;
231 232 233 234 235 236 237
		ret = 0;
		goto out;
	}

	memset(im, 0, sizeof(*im));
	memcpy(im->im_name, name, namelen);

238
	im->im_type = h->h_type;
239 240 241 242 243 244 245 246
	im->im_conv = IDMAP_CONV_NAMETOID;

	memset(&msg, 0, sizeof(msg));
	msg.data = im;
	msg.len = sizeof(*im);

	add_wait_queue(&idmap->idmap_wq, &wq);
	if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) {
247
		remove_wait_queue(&idmap->idmap_wq, &wq);
248 249 250
		goto out;
	}

251
	set_current_state(TASK_UNINTERRUPTIBLE);
252 253
	up(&idmap->idmap_im_lock);
	schedule();
254 255
	current->state = TASK_RUNNING;
	remove_wait_queue(&idmap->idmap_wq, &wq);
256 257 258 259
	down(&idmap->idmap_im_lock);

	if (im->im_status & IDMAP_STATUS_SUCCESS) {
		*id = im->im_id;
260
		ret = 0;
261 262 263 264 265 266 267 268 269 270 271 272
	}

 out:
	memset(im, 0, sizeof(*im));
	up(&idmap->idmap_im_lock);
	up(&idmap->idmap_lock);
	return (ret);
}

/*
 * ID -> Name
 */
273 274 275
static int
nfs_idmap_name(struct idmap *idmap, struct idmap_hashtable *h,
		__u32 id, char *name)
276 277 278
{
	struct rpc_pipe_msg msg;
	struct idmap_msg *im;
279
	struct idmap_hashent *he;
280
	DECLARE_WAITQUEUE(wq, current);
281 282
	int ret = -EIO;
	unsigned int len;
283 284 285 286 287 288

	im = &idmap->idmap_im;

	down(&idmap->idmap_lock);
	down(&idmap->idmap_im_lock);

289 290 291 292
	he = idmap_lookup_id(h, id);
	if (he != 0) {
		memcpy(name, he->ih_name, he->ih_namelen);
		ret = he->ih_namelen;
293 294 295 296
		goto out;
	}

	memset(im, 0, sizeof(*im));
297
	im->im_type = h->h_type;
298 299 300 301 302 303 304 305 306 307
	im->im_conv = IDMAP_CONV_IDTONAME;
	im->im_id = id;

	memset(&msg, 0, sizeof(msg));
	msg.data = im;
	msg.len = sizeof(*im);

	add_wait_queue(&idmap->idmap_wq, &wq);

	if (rpc_queue_upcall(idmap->idmap_dentry->d_inode, &msg) < 0) {
308
		remove_wait_queue(&idmap->idmap_wq, &wq);
309 310 311
		goto out;
	}

312
	set_current_state(TASK_UNINTERRUPTIBLE);
313 314
	up(&idmap->idmap_im_lock);
	schedule();
315 316
	current->state = TASK_RUNNING;
	remove_wait_queue(&idmap->idmap_wq, &wq);
317 318 319
	down(&idmap->idmap_im_lock);

	if (im->im_status & IDMAP_STATUS_SUCCESS) {
320
		if ((len = strnlen(im->im_name, IDMAP_NAMESZ)) == 0)
321 322
			goto out;
		memcpy(name, im->im_name, len);
323
		ret = len;
324 325 326 327 328 329
	}

 out:
	memset(im, 0, sizeof(*im));
	up(&idmap->idmap_im_lock);
	up(&idmap->idmap_lock);
330
	return ret;
331 332
}

333
/* RPC pipefs upcall/downcall routines */
334 335
static ssize_t
idmap_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
336
    char __user *dst, size_t buflen)
337 338 339 340 341 342 343 344 345
{
        char *data = (char *)msg->data + msg->copied;
        ssize_t mlen = msg->len - msg->copied;
        ssize_t left;

        if (mlen > buflen)
                mlen = buflen;

        left = copy_to_user(dst, data, mlen);
346 347 348 349 350 351 352 353
	if (left < 0) {
		msg->errno = left;
		return left;
	}
	mlen -= left;
	msg->copied += mlen;
	msg->errno = 0;
        return mlen;
354 355 356
}

static ssize_t
357
idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
358 359
{
        struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode);
360
	struct idmap *idmap = (struct idmap *)rpci->private;
361
	struct idmap_msg im_in, *im = &idmap->idmap_im;
362 363 364 365
	struct idmap_hashtable *h;
	struct idmap_hashent *he = NULL;
	int namelen_in;
	int ret;
366 367 368 369 370 371 372 373 374

        if (mlen != sizeof(im_in))
                return (-ENOSPC);

        if (copy_from_user(&im_in, src, mlen) != 0)
		return (-EFAULT);

	down(&idmap->idmap_im_lock);

375 376 377 378 379 380 381 382 383 384 385 386 387
	ret = mlen;
	im->im_status = im_in.im_status;
	/* If we got an error, terminate now, and wake up pending upcalls */
	if (!(im_in.im_status & IDMAP_STATUS_SUCCESS)) {
		wake_up(&idmap->idmap_wq);
		goto out;
	}

	/* Sanity checking of strings */
	ret = -EINVAL;
	namelen_in = strnlen(im_in.im_name, IDMAP_NAMESZ);
	if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ)
		goto out;
388

389 390 391 392 393 394 395 396 397 398
	switch (im_in.im_type) {
		case IDMAP_TYPE_USER:
			h = &idmap->idmap_user_hash;
			break;
		case IDMAP_TYPE_GROUP:
			h = &idmap->idmap_group_hash;
			break;
		default:
			goto out;
	}
399 400 401

	switch (im_in.im_conv) {
	case IDMAP_CONV_IDTONAME:
402 403 404 405 406 407 408 409 410 411
		/* Did we match the current upcall? */
		if (im->im_conv == IDMAP_CONV_IDTONAME
				&& im->im_type == im_in.im_type
				&& im->im_id == im_in.im_id) {
			/* Yes: copy string, including the terminating '\0'  */
			memcpy(im->im_name, im_in.im_name, namelen_in);
			im->im_name[namelen_in] = '\0';
			wake_up(&idmap->idmap_wq);
		}
		he = idmap_alloc_id(h, im_in.im_id);
412 413
		break;
	case IDMAP_CONV_NAMETOID:
414 415 416 417 418 419 420 421 422
		/* Did we match the current upcall? */
		if (im->im_conv == IDMAP_CONV_NAMETOID
				&& im->im_type == im_in.im_type
				&& strnlen(im->im_name, IDMAP_NAMESZ) == namelen_in
				&& memcmp(im->im_name, im_in.im_name, namelen_in) == 0) {
			im->im_id = im_in.im_id;
			wake_up(&idmap->idmap_wq);
		}
		he = idmap_alloc_name(h, im_in.im_name, namelen_in);
423 424
		break;
	default:
425
		goto out;
426 427
	}

428 429 430 431 432
	/* If the entry is valid, also copy it to the cache */
	if (he != NULL)
		idmap_update_entry(he, im_in.im_name, namelen_in, im_in.im_id);
	ret = mlen;
out:
433
	up(&idmap->idmap_im_lock);
434
	return ret;
435 436 437 438 439 440 441 442
}

void
idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
	struct idmap_msg *im = msg->data;
	struct idmap *idmap = container_of(im, struct idmap, idmap_im); 

443 444
	if (msg->errno >= 0)
		return;
445 446 447 448 449 450 451 452 453 454 455
	down(&idmap->idmap_im_lock);
	im->im_status = IDMAP_STATUS_LOOKUPFAIL;
	wake_up(&idmap->idmap_wq);
	up(&idmap->idmap_im_lock);
}

/* 
 * Fowler/Noll/Vo hash
 *    http://www.isthe.com/chongo/tech/comp/fnv/
 */

456 457
#define FNV_P_32 ((unsigned int)0x01000193) /* 16777619 */
#define FNV_1_32 ((unsigned int)0x811c9dc5) /* 2166136261 */
458

459
static unsigned int fnvhash32(const void *buf, size_t buflen)
460
{
461 462
	const unsigned char *p, *end = (const unsigned char *)buf + buflen;
	unsigned int hash = FNV_1_32;
463 464 465

	for (p = buf; p < end; p++) {
		hash *= FNV_P_32;
466
		hash ^= (unsigned int)*p;
467 468 469 470 471
	}

	return (hash);
}

472
int nfs_map_name_to_uid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid)
473
{
474
	struct idmap *idmap = clp->cl_idmap;
475

476 477
	return nfs_idmap_id(idmap, &idmap->idmap_user_hash, name, namelen, uid);
}
478

479
int nfs_map_group_to_gid(struct nfs4_client *clp, const char *name, size_t namelen, __u32 *uid)
480
{
481
	struct idmap *idmap = clp->cl_idmap;
482

483 484
	return nfs_idmap_id(idmap, &idmap->idmap_group_hash, name, namelen, uid);
}
485

486
int nfs_map_uid_to_name(struct nfs4_client *clp, __u32 uid, char *buf)
487
{
488
	struct idmap *idmap = clp->cl_idmap;
489

490 491
	return nfs_idmap_name(idmap, &idmap->idmap_user_hash, uid, buf);
}
492
int nfs_map_gid_to_group(struct nfs4_client *clp, __u32 uid, char *buf)
493
{
494
	struct idmap *idmap = clp->cl_idmap;
495

496
	return nfs_idmap_name(idmap, &idmap->idmap_group_hash, uid, buf);
497
}
498