file.c 13.6 KB
Newer Older
G
Greg Kroah-Hartman 已提交
1
// SPDX-License-Identifier: GPL-2.0
L
Linus Torvalds 已提交
2
/*
T
Tejun Heo 已提交
3 4 5 6 7 8 9
 * 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>
 *
 * Please see Documentation/filesystems/sysfs.txt for more information.
L
Linus Torvalds 已提交
10 11 12 13
 */

#include <linux/module.h>
#include <linux/kobject.h>
14
#include <linux/slab.h>
15
#include <linux/list.h>
16
#include <linux/mutex.h>
17
#include <linux/seq_file.h>
L
Linus Torvalds 已提交
18 19

#include "sysfs.h"
20
#include "../kernfs/kernfs-internal.h"
T
Tejun Heo 已提交
21

T
Tejun Heo 已提交
22
/*
23
 * Determine ktype->sysfs_ops for the given kernfs_node.  This function
T
Tejun Heo 已提交
24 25
 * must be called while holding an active reference.
 */
26
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
T
Tejun Heo 已提交
27
{
28
	struct kobject *kobj = kn->parent->priv;
T
Tejun Heo 已提交
29

T
Tejun Heo 已提交
30
	if (kn->flags & KERNFS_LOCKDEP)
31
		lockdep_assert_held(kn);
T
Tejun Heo 已提交
32 33 34
	return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
}

35 36 37 38
/*
 * 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.
L
Linus Torvalds 已提交
39
 */
40
static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
L
Linus Torvalds 已提交
41
{
42
	struct kernfs_open_file *of = sf->private;
43
	struct kobject *kobj = of->kn->parent->priv;
44
	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
L
Linus Torvalds 已提交
45
	ssize_t count;
46
	char *buf;
L
Linus Torvalds 已提交
47

48
	/* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
49 50 51 52 53
	count = seq_get_buf(sf, &buf);
	if (count < PAGE_SIZE) {
		seq_commit(sf, -1);
		return 0;
	}
54
	memset(buf, 0, PAGE_SIZE);
L
Linus Torvalds 已提交
55

56
	/*
57 58
	 * Invoke show().  Control may reach here via seq file lseek even
	 * if @ops->show() isn't implemented.
59
	 */
60
	if (ops->show) {
61
		count = ops->show(kobj, of->kn->priv, buf);
62 63 64
		if (count < 0)
			return count;
	}
65

66 67 68 69
	/*
	 * The code works fine with PAGE_SIZE return but it's likely to
	 * indicate truncated result or overflow in normal use cases.
	 */
70
	if (count >= (ssize_t)PAGE_SIZE) {
71 72
		printk("fill_read_buffer: %pS returned bad count\n",
				ops->show);
73 74 75
		/* Try to struggle along */
		count = PAGE_SIZE - 1;
	}
76 77
	seq_commit(sf, count);
	return 0;
L
Linus Torvalds 已提交
78 79
}

80
static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
81
				 size_t count, loff_t pos)
T
Tejun Heo 已提交
82
{
83
	struct bin_attribute *battr = of->kn->priv;
84
	struct kobject *kobj = of->kn->parent->priv;
85
	loff_t size = file_inode(of->file)->i_size;
T
Tejun Heo 已提交
86

87
	if (!count)
T
Tejun Heo 已提交
88 89 90
		return 0;

	if (size) {
91
		if (pos >= size)
T
Tejun Heo 已提交
92
			return 0;
93 94
		if (pos + count > size)
			count = size - pos;
T
Tejun Heo 已提交
95 96
	}

97 98 99 100 101 102
	if (!battr->read)
		return -EIO;

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

103 104 105 106 107 108
/* 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;
109
	ssize_t len;
110 111 112 113 114

	/*
	 * If buf != of->prealloc_buf, we don't know how
	 * large it is, so cannot safely pass it to ->show
	 */
115
	if (WARN_ON_ONCE(buf != of->prealloc_buf))
116
		return 0;
117
	len = ops->show(kobj, of->kn->priv, buf);
118 119
	if (len < 0)
		return len;
120 121 122 123 124 125
	if (pos) {
		if (len <= pos)
			return 0;
		len -= pos;
		memmove(buf, buf + pos, len);
	}
126
	return min_t(ssize_t, count, len);
127 128
}

129
/* kernfs write callback for regular sysfs files */
130
static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
131
			      size_t count, loff_t pos)
L
Linus Torvalds 已提交
132
{
133
	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
134
	struct kobject *kobj = of->kn->parent->priv;
135

136 137
	if (!count)
		return 0;
138

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

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

150 151
	if (size) {
		if (size <= pos)
152
			return -EFBIG;
153
		count = min_t(ssize_t, count, size - pos);
154
	}
155 156
	if (!count)
		return 0;
157

158 159
	if (!battr->write)
		return -EIO;
L
Linus Torvalds 已提交
160

161
	return battr->write(of->file, kobj, battr, buf, pos, count);
L
Linus Torvalds 已提交
162 163
}

164
static int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
165 166
			     struct vm_area_struct *vma)
{
167
	struct bin_attribute *battr = of->kn->priv;
168
	struct kobject *kobj = of->kn->parent->priv;
169 170 171 172

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

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

177 178
	if (kn && dir)
		kn = kernfs_find_and_get(kn, dir);
179
	else
180
		kernfs_get(kn);
181

182 183 184 185
	if (kn && attr) {
		tmp = kernfs_find_and_get(kn, attr);
		kernfs_put(kn);
		kn = tmp;
186
	}
187

188 189 190
	if (kn) {
		kernfs_notify(kn);
		kernfs_put(kn);
191
	}
192 193 194
}
EXPORT_SYMBOL_GPL(sysfs_notify);

T
Tejun Heo 已提交
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
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,
};

211 212 213 214 215
static const struct kernfs_ops sysfs_prealloc_kfops_ro = {
	.read		= sysfs_kf_read,
	.prealloc	= true,
};

216 217 218 219 220 221
static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
	.write		= sysfs_kf_write,
	.prealloc	= true,
};

static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
222
	.read		= sysfs_kf_read,
223 224 225 226
	.write		= sysfs_kf_write,
	.prealloc	= true,
};

T
Tejun Heo 已提交
227 228 229 230 231 232 233 234 235 236 237
static const struct kernfs_ops sysfs_bin_kfops_ro = {
	.read		= sysfs_kf_bin_read,
};

static const struct kernfs_ops sysfs_bin_kfops_wo = {
	.write		= sysfs_kf_bin_write,
};

static const struct kernfs_ops sysfs_bin_kfops_rw = {
	.read		= sysfs_kf_bin_read,
	.write		= sysfs_kf_bin_write,
238 239 240 241 242
};

static const struct kernfs_ops sysfs_bin_kfops_mmap = {
	.read		= sysfs_kf_bin_read,
	.write		= sysfs_kf_bin_write,
T
Tejun Heo 已提交
243 244 245
	.mmap		= sysfs_kf_bin_mmap,
};

246
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
247
			   const struct attribute *attr, bool is_bin,
248
			   umode_t mode, kuid_t uid, kgid_t gid, const void *ns)
L
Linus Torvalds 已提交
249
{
250
	struct lock_class_key *key = NULL;
T
Tejun Heo 已提交
251
	const struct kernfs_ops *ops;
252
	struct kernfs_node *kn;
253
	loff_t size;
L
Linus Torvalds 已提交
254

255
	if (!is_bin) {
256
		struct kobject *kobj = parent->priv;
T
Tejun Heo 已提交
257 258 259 260 261 262 263 264
		const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;

		/* every kobject with an attribute needs a ktype assigned */
		if (WARN(!sysfs_ops, KERN_ERR
			 "missing sysfs attribute operations for kobject: %s\n",
			 kobject_name(kobj)))
			return -EINVAL;

265 266 267 268 269
		if (sysfs_ops->show && sysfs_ops->store) {
			if (mode & SYSFS_PREALLOC)
				ops = &sysfs_prealloc_kfops_rw;
			else
				ops = &sysfs_file_kfops_rw;
270 271 272 273 274 275
		} else if (sysfs_ops->show) {
			if (mode & SYSFS_PREALLOC)
				ops = &sysfs_prealloc_kfops_ro;
			else
				ops = &sysfs_file_kfops_ro;
		} else if (sysfs_ops->store) {
276 277 278 279 280
			if (mode & SYSFS_PREALLOC)
				ops = &sysfs_prealloc_kfops_wo;
			else
				ops = &sysfs_file_kfops_wo;
		} else
T
Tejun Heo 已提交
281
			ops = &sysfs_file_kfops_empty;
282 283

		size = PAGE_SIZE;
T
Tejun Heo 已提交
284 285 286
	} else {
		struct bin_attribute *battr = (void *)attr;

287 288 289
		if (battr->mmap)
			ops = &sysfs_bin_kfops_mmap;
		else if (battr->read && battr->write)
T
Tejun Heo 已提交
290 291 292 293 294 295 296
			ops = &sysfs_bin_kfops_rw;
		else if (battr->read)
			ops = &sysfs_bin_kfops_ro;
		else if (battr->write)
			ops = &sysfs_bin_kfops_wo;
		else
			ops = &sysfs_file_kfops_empty;
297 298

		size = battr->size;
T
Tejun Heo 已提交
299 300
	}

301 302 303 304
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	if (!attr->ignore_lockdep)
		key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif
305 306

	kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid,
307
				  size, ops, (void *)attr, ns, key);
308 309 310 311
	if (IS_ERR(kn)) {
		if (PTR_ERR(kn) == -EEXIST)
			sysfs_warn_dup(parent, attr->name);
		return PTR_ERR(kn);
312 313 314 315
	}
	return 0;
}

L
Linus Torvalds 已提交
316
/**
317 318 319 320
 * sysfs_create_file_ns - create an attribute file for an object with custom ns
 * @kobj: object we're creating for
 * @attr: attribute descriptor
 * @ns: namespace the new file should belong to
L
Linus Torvalds 已提交
321
 */
322 323
int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
			 const void *ns)
L
Linus Torvalds 已提交
324
{
325 326 327
	kuid_t uid;
	kgid_t gid;

328 329
	if (WARN_ON(!kobj || !kobj->sd || !attr))
		return -EINVAL;
L
Linus Torvalds 已提交
330

331 332 333
	kobject_get_ownership(kobj, &uid, &gid);
	return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode,
				      uid, gid, ns);
L
Linus Torvalds 已提交
334 335

}
336
EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
L
Linus Torvalds 已提交
337

338 339 340 341 342 343 344 345 346 347 348 349
int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr)
{
	int err = 0;
	int i;

	for (i = 0; ptr[i] && !err; i++)
		err = sysfs_create_file(kobj, ptr[i]);
	if (err)
		while (--i >= 0)
			sysfs_remove_file(kobj, ptr[i]);
	return err;
}
350
EXPORT_SYMBOL_GPL(sysfs_create_files);
L
Linus Torvalds 已提交
351

352 353 354 355 356 357 358 359 360
/**
 * sysfs_add_file_to_group - add an attribute file to a pre-existing group.
 * @kobj: object we're acting for.
 * @attr: attribute descriptor.
 * @group: group name.
 */
int sysfs_add_file_to_group(struct kobject *kobj,
		const struct attribute *attr, const char *group)
{
361
	struct kernfs_node *parent;
362 363
	kuid_t uid;
	kgid_t gid;
364 365
	int error;

366
	if (group) {
367
		parent = kernfs_find_and_get(kobj->sd, group);
368
	} else {
369 370
		parent = kobj->sd;
		kernfs_get(parent);
371
	}
372

373
	if (!parent)
374 375
		return -ENOENT;

376
	kobject_get_ownership(kobj, &uid, &gid);
377
	error = sysfs_add_file_mode_ns(parent, attr, false,
378
				       attr->mode, uid, gid, NULL);
379
	kernfs_put(parent);
380

381 382 383 384
	return error;
}
EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);

385 386 387 388 389 390 391
/**
 * sysfs_chmod_file - update the modified mode value on an object attribute.
 * @kobj: object we're acting for.
 * @attr: attribute descriptor.
 * @mode: file permissions.
 *
 */
392
int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
A
Al Viro 已提交
393
		     umode_t mode)
394
{
395
	struct kernfs_node *kn;
396
	struct iattr newattrs;
397 398
	int rc;

399 400
	kn = kernfs_find_and_get(kobj->sd, attr->name);
	if (!kn)
401
		return -ENOENT;
402

403
	newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO);
404
	newattrs.ia_valid = ATTR_MODE;
405

406
	rc = kernfs_setattr(kn, &newattrs);
407

408
	kernfs_put(kn);
409
	return rc;
410 411 412
}
EXPORT_SYMBOL_GPL(sysfs_chmod_file);

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
/**
 * sysfs_break_active_protection - break "active" protection
 * @kobj: The kernel object @attr is associated with.
 * @attr: The attribute to break the "active" protection for.
 *
 * With sysfs, just like kernfs, deletion of an attribute is postponed until
 * all active .show() and .store() callbacks have finished unless this function
 * is called. Hence this function is useful in methods that implement self
 * deletion.
 */
struct kernfs_node *sysfs_break_active_protection(struct kobject *kobj,
						  const struct attribute *attr)
{
	struct kernfs_node *kn;

	kobject_get(kobj);
	kn = kernfs_find_and_get(kobj->sd, attr->name);
	if (kn)
		kernfs_break_active_protection(kn);
	return kn;
}
EXPORT_SYMBOL_GPL(sysfs_break_active_protection);

/**
 * sysfs_unbreak_active_protection - restore "active" protection
 * @kn: Pointer returned by sysfs_break_active_protection().
 *
 * Undo the effects of sysfs_break_active_protection(). Since this function
 * calls kernfs_put() on the kernfs node that corresponds to the 'attr'
 * argument passed to sysfs_break_active_protection() that attribute may have
 * been removed between the sysfs_break_active_protection() and
 * sysfs_unbreak_active_protection() calls, it is not safe to access @kn after
 * this function has returned.
 */
void sysfs_unbreak_active_protection(struct kernfs_node *kn)
{
	struct kobject *kobj = kn->parent->priv;

	kernfs_unbreak_active_protection(kn);
	kernfs_put(kn);
	kobject_put(kobj);
}
EXPORT_SYMBOL_GPL(sysfs_unbreak_active_protection);

L
Linus Torvalds 已提交
457
/**
458 459 460 461
 * sysfs_remove_file_ns - remove an object attribute with a custom ns tag
 * @kobj: object we're acting for
 * @attr: attribute descriptor
 * @ns: namespace tag of the file to remove
L
Linus Torvalds 已提交
462
 *
463
 * Hash the attribute name and namespace tag and kill the victim.
L
Linus Torvalds 已提交
464
 */
465 466
void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
			  const void *ns)
L
Linus Torvalds 已提交
467
{
468
	struct kernfs_node *parent = kobj->sd;
469

470
	kernfs_remove_by_name_ns(parent, attr->name, ns);
L
Linus Torvalds 已提交
471
}
472
EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
L
Linus Torvalds 已提交
473

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
/**
 * sysfs_remove_file_self - remove an object attribute from its own method
 * @kobj: object we're acting for
 * @attr: attribute descriptor
 *
 * See kernfs_remove_self() for details.
 */
bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr)
{
	struct kernfs_node *parent = kobj->sd;
	struct kernfs_node *kn;
	bool ret;

	kn = kernfs_find_and_get(parent, attr->name);
	if (WARN_ON_ONCE(!kn))
		return false;

	ret = kernfs_remove_self(kn);

	kernfs_put(kn);
	return ret;
}

497
void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr)
498 499 500 501 502
{
	int i;
	for (i = 0; ptr[i]; i++)
		sysfs_remove_file(kobj, ptr[i]);
}
503
EXPORT_SYMBOL_GPL(sysfs_remove_files);
L
Linus Torvalds 已提交
504

505 506 507 508 509 510 511 512 513
/**
 * sysfs_remove_file_from_group - remove an attribute file from a group.
 * @kobj: object we're acting for.
 * @attr: attribute descriptor.
 * @group: group name.
 */
void sysfs_remove_file_from_group(struct kobject *kobj,
		const struct attribute *attr, const char *group)
{
514
	struct kernfs_node *parent;
515

516
	if (group) {
517
		parent = kernfs_find_and_get(kobj->sd, group);
518
	} else {
519 520
		parent = kobj->sd;
		kernfs_get(parent);
521 522
	}

523 524 525
	if (parent) {
		kernfs_remove_by_name(parent, attr->name);
		kernfs_put(parent);
526 527 528 529
	}
}
EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);

530 531 532 533 534 535 536 537
/**
 *	sysfs_create_bin_file - create binary file for object.
 *	@kobj:	object.
 *	@attr:	attribute descriptor.
 */
int sysfs_create_bin_file(struct kobject *kobj,
			  const struct bin_attribute *attr)
{
538 539 540
	kuid_t uid;
	kgid_t gid;

541 542
	if (WARN_ON(!kobj || !kobj->sd || !attr))
		return -EINVAL;
543

544 545 546
	kobject_get_ownership(kobj, &uid, &gid);
	return sysfs_add_file_mode_ns(kobj->sd, &attr->attr, true,
				      attr->attr.mode, uid, gid, NULL);
547 548 549 550 551 552 553 554 555 556 557
}
EXPORT_SYMBOL_GPL(sysfs_create_bin_file);

/**
 *	sysfs_remove_bin_file - remove binary file for object.
 *	@kobj:	object.
 *	@attr:	attribute descriptor.
 */
void sysfs_remove_bin_file(struct kobject *kobj,
			   const struct bin_attribute *attr)
{
558
	kernfs_remove_by_name(kobj->sd, attr->attr.name);
559 560
}
EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);