summaryrefslogtreecommitdiffstats
path: root/ipc
AgeCommit message (Expand)Author
2012-07-23switch dentry_open() to struct path, make it grab references itselfAl Viro
2012-07-14don't pass nameidata * to vfs_create()Al Viro
2012-07-14don't pass nameidata to ->create()Al Viro
2012-06-07ipc: shm: restore MADV_REMOVE functionality on shared memory segmentsWill Deacon
2012-06-01Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/vir...Linus Torvalds
2012-06-01switch aio and shm to do_mmap_pgoff(), make do_mmap() staticAl Viro
2012-06-01take security_mmap_file() outside of ->mmap_semAl Viro
2012-05-31ipc/mqueue: add rbtree node caching supportDoug Ledford
2012-05-31ipc/mqueue: strengthen checks on mqueue creationDoug Ledford
2012-05-31ipc/mqueue: correct mq_attr_ok testDoug Ledford
2012-05-31ipc/mqueue: improve performance of send/recvDoug Ledford
2012-05-31mqueue: separate mqueue default value from maximum valueKOSAKI Motohiro
2012-05-31mqueue: don't use kmalloc with KMALLOC_MAX_SIZEKOSAKI Motohiro
2012-05-31ipc/mqueue: update maximums for the mqueue subsystemDoug Ledford
2012-05-31ipc/mqueue: enforce hard limitsDoug Ledford
2012-05-31ipc/mqueue: switch back to using non-max values on createDoug Ledford
2012-05-31ipc/mqueue: cleanup definition names and locationsDoug Ledford
2012-05-28Merge tag 'writeback' of git://git.kernel.org/pub/scm/linux/kernel/git/wfg/linuxLinus Torvalds
2012-05-06vfs: Rename end_writeback() to clear_inode()Jan Kara
2012-05-03userns: Replace user_ns_map_uid and user_ns_map_gid with from_kuid and from_kgidEric W. Biederman
2012-04-07mqueue: Explicitly capture the user namespace to send the notification to.Eric W. Biederman
2012-04-07userns: Use cred->user_ns instead of cred->user->user_nsEric W. Biederman
2012-03-29Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tileLinus Torvalds
2012-03-22Merge branch 'akpm' (Andrew's patch-bomb)Linus Torvalds
2012-03-21hugetlbfs: fix alignment of huge page requestsSteven Truelove
2012-03-21Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/vir...Linus Torvalds
2012-03-20switch open-coded instances of d_make_root() to new helperAl Viro
2012-03-15[PATCH v3] ipc: provide generic compat versions of IPC syscallsChris Metcalf
2012-02-14security: trim security.hAl Viro
2012-01-23SHM_UNLOCK: fix Unevictable pages stranded after swapHugh Dickins
2012-01-23SHM_UNLOCK: fix long unpreemptible sectionHugh Dickins
2012-01-23ipc/mqueue: simplify reading msgqueue limitDavidlohr Bueso
2012-01-10user namespace: make signal.c respect user namespacesSerge E. Hallyn
2012-01-03switch mq_open() to umode_tAl Viro
2012-01-03mqueue: propagate umode_tAl Viro
2012-01-03switch ->create() to umode_tAl Viro
2012-01-03vfs: fix the stupidity with i_dentry in inode destructorsAl Viro
2011-12-09... and the same kind of leak for mqueueAl Viro
2011-11-02ipc/sem.c: remove private structures from public header fileManfred Spraul
2011-11-02ipc/sem.c: handle spurious wakeupsManfred Spraul
2011-11-02ipc/sem.c: fix return code race with semop vs. semop +semctl(IPC_RMID)Manfred Spraul
2011-10-31ipc/mqueue.c: fix wrong use of schedule_hrtimeout_range_clock()Wanlong Gao
2011-08-04Do 'shm_init_ns()' in an early pure_initcallLinus Torvalds
2011-08-03shm: optimize exit_shm()Vasiliy Kulikov
2011-08-03shm: fix wrong testsVasiliy Kulikov
2011-07-30shm: optimize locking and ipc_namespace gettingVasiliy Kulikov
2011-07-30shm: handle separate PID namespaces caseVasiliy Kulikov
2011-07-26ipc: introduce shm_rmid_forced sysctlVasiliy Kulikov
2011-07-26ipc/mqueue.c: fix mq_open() return valueJiri Slaby
2011-07-26ipc/mqueue.c: refactor failure handlingJiri Slaby
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
/*
 * fs/sysfs/file.c - sysfs regular (text) file implementation
 *
 * Copyright (c) 2001-3 Patrick Mochel
 * Copyright (c) 2007 SUSE Linux Products GmbH
 * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
 *
 * This file is released under the GPLv2.
 *
 * Please see Documentation/filesystems/sysfs.txt for more information.
 */

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/kallsyms.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>

#include "sysfs.h"
#include "../kernfs/kernfs-internal.h"

/*
 * Determine ktype->sysfs_ops for the given kernfs_node.  This function
 * must be called while holding an active reference.
 */
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{
	struct kobject *kobj = kn->parent->priv;

	if (kn->flags & KERNFS_LOCKDEP)
		lockdep_assert_held(kn);
	return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
}

/*
 * Reads on sysfs are handled through seq_file, which takes care of hairy
 * details like buffering and seeking.  The following function pipes
 * sysfs_ops->show() result through seq_file.
 */
static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
{
	struct kernfs_open_file *of = sf->private;
	struct kobject *kobj = of->kn->parent->priv;
	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
	ssize_t count;
	char *buf;

	/* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
	count = seq_get_buf(sf, &buf);
	if (count < PAGE_SIZE) {
		seq_commit(sf, -1);
		return 0;
	}
	memset(buf, 0, PAGE_SIZE);

	/*
	 * Invoke show().  Control may reach here via seq file lseek even
	 * if @ops->show() isn't implemented.
	 */
	if (ops->show) {
		count = ops->show(kobj, of->kn->priv, buf);
		if (count < 0)
			return count;
	}

	/*
	 * The code works fine with PAGE_SIZE return but it's likely to
	 * indicate truncated result or overflow in normal use cases.
	 */
	if (count >= (ssize_t)PAGE_SIZE) {
		print_symbol("fill_read_buffer: %s returned bad count\n",
			(unsigned long)ops->show);
		/* Try to struggle along */
		count = PAGE_SIZE - 1;
	}
	seq_commit(sf, count);
	return 0;
}

static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
				 size_t count, loff_t pos)
{
	struct bin_attribute *battr = of->kn->priv;
	struct kobject *kobj = of->kn->parent->priv;
	loff_t size = file_inode(of->file)->i_size;

	if (!count)
		return 0;

	if (size) {
		if (pos >= size)
			return 0;
		if (pos + count > size)
			count = size - pos;
	}

	if (!battr->read)
		return -EIO;

	return battr->read(of->file, kobj, battr, buf, pos, count);
}

/* kernfs read callback for regular sysfs files with pre-alloc */
static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
			     size_t count, loff_t pos)
{
	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
	struct kobject *kobj = of->kn->parent->priv;
	ssize_t len;

	/*
	 * If buf != of->prealloc_buf, we don't know how
	 * large it is, so cannot safely pass it to ->show
	 */
	if (WARN_ON_ONCE(buf != of->prealloc_buf))
		return 0;
	len = ops->show(kobj, of->kn->priv, buf);
	if (len < 0)
		return len;
	if (pos) {
		if (len <= pos)
			return 0;
		len -= pos;
		memmove(buf, buf + pos, len);
	}
	return min_t(ssize_t, count, len);
}

/* kernfs write callback for regular sysfs files */
static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
			      size_t count, loff_t pos)
{
	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
	struct kobject *kobj = of->kn->parent->priv;

	if (!count)
		return 0;

	return ops->store(kobj, of->kn->priv, buf, count);
}

/* kernfs write callback for bin sysfs files */
static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf,
				  size_t count, loff_t pos)
{
	struct bin_attribute *battr = of->kn->priv;
	struct kobject *kobj = of->kn->parent->priv;
	loff_t size = file_inode(of->file)->i_size;

	if (size) {
		if (size <= pos)
			return -EFBIG;
		count = min_t(ssize_t, count, size - pos);
	}
	if (!count)
		return 0;

	if (!battr->write)
		return -EIO;

	return battr->write(of->file, kobj, battr, buf, pos, count);
}

static int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
			     struct vm_area_struct *vma)
{
	struct bin_attribute *battr = of->kn->priv;
	struct kobject *kobj = of->kn->parent->priv;

	return battr->mmap(of->file, kobj, battr, vma);
}

void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr)
{
	struct kernfs_node *kn = kobj->sd, *tmp;

	if (kn && dir)
		kn = kernfs_find_and_get(kn, dir);
	else
		kernfs_get(kn);

	if (kn && attr) {
		tmp = kernfs_find_and_get(kn, attr);
		kernfs_put(kn);
		kn = tmp;
	}

	if (kn) {
		kernfs_notify(kn);
		kernfs_put(kn);
	}
}
EXPORT_SYMBOL_GPL(sysfs_notify);

static const struct kernfs_ops sysfs_file_kfops_empty = {
};

static const struct kernfs_ops sysfs_file_kfops_ro = {
	.seq_show	= sysfs_kf_seq_show,
};

static const struct kernfs_ops sysfs_file_kfops_wo = {
	.write		= sysfs_kf_write,
};

static const struct kernfs_ops sysfs_file_kfops_rw = {
	.seq_show	= sysfs_kf_seq_show,
	.write		= sysfs_kf_write,
};

static const struct kernfs_ops sysfs_prealloc_kfops_ro = {
	.read		= sysfs_kf_read,
	.prealloc	= true,
};

static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
	.write		= sysfs_kf_write,
	.prealloc	= true,
};

static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
	.read		= sysfs_kf_read,
	.write		= sysfs_kf_write,
	.prealloc	= true,
};

static const struct kernfs_ops sysfs_bin_kfops_ro = {
	.read		= sysfs_kf_bin_read,
};

static const struct kernfs_ops sysfs_bin_kfops_wo = {
	.write		=