162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) International Business Machines Corp., 2006
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Artem Bityutskiy (Битюцкий Артём)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * This file includes implementation of UBI character device operations.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * There are two kinds of character devices in UBI: UBI character devices and
1262306a36Sopenharmony_ci * UBI volume character devices. UBI character devices allow users to
1362306a36Sopenharmony_ci * manipulate whole volumes: create, remove, and re-size them. Volume character
1462306a36Sopenharmony_ci * devices provide volume I/O capabilities.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Major and minor numbers are assigned dynamically to both UBI and volume
1762306a36Sopenharmony_ci * character devices.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Well, there is the third kind of character devices - the UBI control
2062306a36Sopenharmony_ci * character device, which allows to manipulate by UBI devices - create and
2162306a36Sopenharmony_ci * delete them. In other words, it is used for attaching and detaching MTD
2262306a36Sopenharmony_ci * devices.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/module.h>
2662306a36Sopenharmony_ci#include <linux/stat.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/ioctl.h>
2962306a36Sopenharmony_ci#include <linux/capability.h>
3062306a36Sopenharmony_ci#include <linux/uaccess.h>
3162306a36Sopenharmony_ci#include <linux/compat.h>
3262306a36Sopenharmony_ci#include <linux/math64.h>
3362306a36Sopenharmony_ci#include <mtd/ubi-user.h>
3462306a36Sopenharmony_ci#include "ubi.h"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/**
3762306a36Sopenharmony_ci * get_exclusive - get exclusive access to an UBI volume.
3862306a36Sopenharmony_ci * @desc: volume descriptor
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * This function changes UBI volume open mode to "exclusive". Returns previous
4162306a36Sopenharmony_ci * mode value (positive integer) in case of success and a negative error code
4262306a36Sopenharmony_ci * in case of failure.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistatic int get_exclusive(struct ubi_volume_desc *desc)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	int users, err;
4762306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	spin_lock(&vol->ubi->volumes_lock);
5062306a36Sopenharmony_ci	users = vol->readers + vol->writers + vol->exclusive + vol->metaonly;
5162306a36Sopenharmony_ci	ubi_assert(users > 0);
5262306a36Sopenharmony_ci	if (users > 1) {
5362306a36Sopenharmony_ci		ubi_err(vol->ubi, "%d users for volume %d", users, vol->vol_id);
5462306a36Sopenharmony_ci		err = -EBUSY;
5562306a36Sopenharmony_ci	} else {
5662306a36Sopenharmony_ci		vol->readers = vol->writers = vol->metaonly = 0;
5762306a36Sopenharmony_ci		vol->exclusive = 1;
5862306a36Sopenharmony_ci		err = desc->mode;
5962306a36Sopenharmony_ci		desc->mode = UBI_EXCLUSIVE;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	spin_unlock(&vol->ubi->volumes_lock);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return err;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/**
6762306a36Sopenharmony_ci * revoke_exclusive - revoke exclusive mode.
6862306a36Sopenharmony_ci * @desc: volume descriptor
6962306a36Sopenharmony_ci * @mode: new mode to switch to
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistatic void revoke_exclusive(struct ubi_volume_desc *desc, int mode)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	spin_lock(&vol->ubi->volumes_lock);
7662306a36Sopenharmony_ci	ubi_assert(vol->readers == 0 && vol->writers == 0 && vol->metaonly == 0);
7762306a36Sopenharmony_ci	ubi_assert(vol->exclusive == 1 && desc->mode == UBI_EXCLUSIVE);
7862306a36Sopenharmony_ci	vol->exclusive = 0;
7962306a36Sopenharmony_ci	if (mode == UBI_READONLY)
8062306a36Sopenharmony_ci		vol->readers = 1;
8162306a36Sopenharmony_ci	else if (mode == UBI_READWRITE)
8262306a36Sopenharmony_ci		vol->writers = 1;
8362306a36Sopenharmony_ci	else if (mode == UBI_METAONLY)
8462306a36Sopenharmony_ci		vol->metaonly = 1;
8562306a36Sopenharmony_ci	else
8662306a36Sopenharmony_ci		vol->exclusive = 1;
8762306a36Sopenharmony_ci	spin_unlock(&vol->ubi->volumes_lock);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	desc->mode = mode;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int vol_cdev_open(struct inode *inode, struct file *file)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct ubi_volume_desc *desc;
9562306a36Sopenharmony_ci	int vol_id = iminor(inode) - 1, mode, ubi_num;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	ubi_num = ubi_major2num(imajor(inode));
9862306a36Sopenharmony_ci	if (ubi_num < 0)
9962306a36Sopenharmony_ci		return ubi_num;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (file->f_mode & FMODE_WRITE)
10262306a36Sopenharmony_ci		mode = UBI_READWRITE;
10362306a36Sopenharmony_ci	else
10462306a36Sopenharmony_ci		mode = UBI_READONLY;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	dbg_gen("open device %d, volume %d, mode %d",
10762306a36Sopenharmony_ci		ubi_num, vol_id, mode);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	desc = ubi_open_volume(ubi_num, vol_id, mode);
11062306a36Sopenharmony_ci	if (IS_ERR(desc))
11162306a36Sopenharmony_ci		return PTR_ERR(desc);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	file->private_data = desc;
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int vol_cdev_release(struct inode *inode, struct file *file)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct ubi_volume_desc *desc = file->private_data;
12062306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	dbg_gen("release device %d, volume %d, mode %d",
12362306a36Sopenharmony_ci		vol->ubi->ubi_num, vol->vol_id, desc->mode);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (vol->updating) {
12662306a36Sopenharmony_ci		ubi_warn(vol->ubi, "update of volume %d not finished, volume is damaged",
12762306a36Sopenharmony_ci			 vol->vol_id);
12862306a36Sopenharmony_ci		ubi_assert(!vol->changing_leb);
12962306a36Sopenharmony_ci		vol->updating = 0;
13062306a36Sopenharmony_ci		vfree(vol->upd_buf);
13162306a36Sopenharmony_ci	} else if (vol->changing_leb) {
13262306a36Sopenharmony_ci		dbg_gen("only %lld of %lld bytes received for atomic LEB change for volume %d:%d, cancel",
13362306a36Sopenharmony_ci			vol->upd_received, vol->upd_bytes, vol->ubi->ubi_num,
13462306a36Sopenharmony_ci			vol->vol_id);
13562306a36Sopenharmony_ci		vol->changing_leb = 0;
13662306a36Sopenharmony_ci		vfree(vol->upd_buf);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	ubi_close_volume(desc);
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct ubi_volume_desc *desc = file->private_data;
14662306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (vol->updating) {
14962306a36Sopenharmony_ci		/* Update is in progress, seeking is prohibited */
15062306a36Sopenharmony_ci		ubi_err(vol->ubi, "updating");
15162306a36Sopenharmony_ci		return -EBUSY;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return fixed_size_llseek(file, offset, origin, vol->used_bytes);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int vol_cdev_fsync(struct file *file, loff_t start, loff_t end,
15862306a36Sopenharmony_ci			  int datasync)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct ubi_volume_desc *desc = file->private_data;
16162306a36Sopenharmony_ci	struct ubi_device *ubi = desc->vol->ubi;
16262306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
16362306a36Sopenharmony_ci	int err;
16462306a36Sopenharmony_ci	inode_lock(inode);
16562306a36Sopenharmony_ci	err = ubi_sync(ubi->ubi_num);
16662306a36Sopenharmony_ci	inode_unlock(inode);
16762306a36Sopenharmony_ci	return err;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
17262306a36Sopenharmony_ci			     loff_t *offp)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct ubi_volume_desc *desc = file->private_data;
17562306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
17662306a36Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
17762306a36Sopenharmony_ci	int err, lnum, off, len,  tbuf_size;
17862306a36Sopenharmony_ci	size_t count_save = count;
17962306a36Sopenharmony_ci	void *tbuf;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	dbg_gen("read %zd bytes from offset %lld of volume %d",
18262306a36Sopenharmony_ci		count, *offp, vol->vol_id);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (vol->updating) {
18562306a36Sopenharmony_ci		ubi_err(vol->ubi, "updating");
18662306a36Sopenharmony_ci		return -EBUSY;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	if (vol->upd_marker) {
18962306a36Sopenharmony_ci		ubi_err(vol->ubi, "damaged volume, update marker is set");
19062306a36Sopenharmony_ci		return -EBADF;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	if (*offp == vol->used_bytes || count == 0)
19362306a36Sopenharmony_ci		return 0;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (vol->corrupted)
19662306a36Sopenharmony_ci		dbg_gen("read from corrupted volume %d", vol->vol_id);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (*offp + count > vol->used_bytes)
19962306a36Sopenharmony_ci		count_save = count = vol->used_bytes - *offp;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	tbuf_size = vol->usable_leb_size;
20262306a36Sopenharmony_ci	if (count < tbuf_size)
20362306a36Sopenharmony_ci		tbuf_size = ALIGN(count, ubi->min_io_size);
20462306a36Sopenharmony_ci	tbuf = vmalloc(tbuf_size);
20562306a36Sopenharmony_ci	if (!tbuf)
20662306a36Sopenharmony_ci		return -ENOMEM;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	len = count > tbuf_size ? tbuf_size : count;
20962306a36Sopenharmony_ci	lnum = div_u64_rem(*offp, vol->usable_leb_size, &off);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	do {
21262306a36Sopenharmony_ci		cond_resched();
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		if (off + len >= vol->usable_leb_size)
21562306a36Sopenharmony_ci			len = vol->usable_leb_size - off;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0);
21862306a36Sopenharmony_ci		if (err)
21962306a36Sopenharmony_ci			break;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		off += len;
22262306a36Sopenharmony_ci		if (off == vol->usable_leb_size) {
22362306a36Sopenharmony_ci			lnum += 1;
22462306a36Sopenharmony_ci			off -= vol->usable_leb_size;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		count -= len;
22862306a36Sopenharmony_ci		*offp += len;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		err = copy_to_user(buf, tbuf, len);
23162306a36Sopenharmony_ci		if (err) {
23262306a36Sopenharmony_ci			err = -EFAULT;
23362306a36Sopenharmony_ci			break;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		buf += len;
23762306a36Sopenharmony_ci		len = count > tbuf_size ? tbuf_size : count;
23862306a36Sopenharmony_ci	} while (count);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	vfree(tbuf);
24162306a36Sopenharmony_ci	return err ? err : count_save - count;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci/*
24562306a36Sopenharmony_ci * This function allows to directly write to dynamic UBI volumes, without
24662306a36Sopenharmony_ci * issuing the volume update operation.
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_cistatic ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
24962306a36Sopenharmony_ci				     size_t count, loff_t *offp)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct ubi_volume_desc *desc = file->private_data;
25262306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
25362306a36Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
25462306a36Sopenharmony_ci	int lnum, off, len, tbuf_size, err = 0;
25562306a36Sopenharmony_ci	size_t count_save = count;
25662306a36Sopenharmony_ci	char *tbuf;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!vol->direct_writes)
25962306a36Sopenharmony_ci		return -EPERM;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	dbg_gen("requested: write %zd bytes to offset %lld of volume %u",
26262306a36Sopenharmony_ci		count, *offp, vol->vol_id);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (vol->vol_type == UBI_STATIC_VOLUME)
26562306a36Sopenharmony_ci		return -EROFS;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	lnum = div_u64_rem(*offp, vol->usable_leb_size, &off);
26862306a36Sopenharmony_ci	if (off & (ubi->min_io_size - 1)) {
26962306a36Sopenharmony_ci		ubi_err(ubi, "unaligned position");
27062306a36Sopenharmony_ci		return -EINVAL;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (*offp + count > vol->used_bytes)
27462306a36Sopenharmony_ci		count_save = count = vol->used_bytes - *offp;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* We can write only in fractions of the minimum I/O unit */
27762306a36Sopenharmony_ci	if (count & (ubi->min_io_size - 1)) {
27862306a36Sopenharmony_ci		ubi_err(ubi, "unaligned write length");
27962306a36Sopenharmony_ci		return -EINVAL;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	tbuf_size = vol->usable_leb_size;
28362306a36Sopenharmony_ci	if (count < tbuf_size)
28462306a36Sopenharmony_ci		tbuf_size = ALIGN(count, ubi->min_io_size);
28562306a36Sopenharmony_ci	tbuf = vmalloc(tbuf_size);
28662306a36Sopenharmony_ci	if (!tbuf)
28762306a36Sopenharmony_ci		return -ENOMEM;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	len = count > tbuf_size ? tbuf_size : count;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	while (count) {
29262306a36Sopenharmony_ci		cond_resched();
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (off + len >= vol->usable_leb_size)
29562306a36Sopenharmony_ci			len = vol->usable_leb_size - off;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		err = copy_from_user(tbuf, buf, len);
29862306a36Sopenharmony_ci		if (err) {
29962306a36Sopenharmony_ci			err = -EFAULT;
30062306a36Sopenharmony_ci			break;
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		err = ubi_eba_write_leb(ubi, vol, lnum, tbuf, off, len);
30462306a36Sopenharmony_ci		if (err)
30562306a36Sopenharmony_ci			break;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		off += len;
30862306a36Sopenharmony_ci		if (off == vol->usable_leb_size) {
30962306a36Sopenharmony_ci			lnum += 1;
31062306a36Sopenharmony_ci			off -= vol->usable_leb_size;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		count -= len;
31462306a36Sopenharmony_ci		*offp += len;
31562306a36Sopenharmony_ci		buf += len;
31662306a36Sopenharmony_ci		len = count > tbuf_size ? tbuf_size : count;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	vfree(tbuf);
32062306a36Sopenharmony_ci	return err ? err : count_save - count;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic ssize_t vol_cdev_write(struct file *file, const char __user *buf,
32462306a36Sopenharmony_ci			      size_t count, loff_t *offp)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	int err = 0;
32762306a36Sopenharmony_ci	struct ubi_volume_desc *desc = file->private_data;
32862306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
32962306a36Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (!vol->updating && !vol->changing_leb)
33262306a36Sopenharmony_ci		return vol_cdev_direct_write(file, buf, count, offp);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (vol->updating)
33562306a36Sopenharmony_ci		err = ubi_more_update_data(ubi, vol, buf, count);
33662306a36Sopenharmony_ci	else
33762306a36Sopenharmony_ci		err = ubi_more_leb_change_data(ubi, vol, buf, count);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (err < 0) {
34062306a36Sopenharmony_ci		ubi_err(ubi, "cannot accept more %zd bytes of data, error %d",
34162306a36Sopenharmony_ci			count, err);
34262306a36Sopenharmony_ci		return err;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (err) {
34662306a36Sopenharmony_ci		/*
34762306a36Sopenharmony_ci		 * The operation is finished, @err contains number of actually
34862306a36Sopenharmony_ci		 * written bytes.
34962306a36Sopenharmony_ci		 */
35062306a36Sopenharmony_ci		count = err;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci		if (vol->changing_leb) {
35362306a36Sopenharmony_ci			revoke_exclusive(desc, UBI_READWRITE);
35462306a36Sopenharmony_ci			return count;
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		/*
35862306a36Sopenharmony_ci		 * We voluntarily do not take into account the skip_check flag
35962306a36Sopenharmony_ci		 * as we want to make sure what we wrote was correctly written.
36062306a36Sopenharmony_ci		 */
36162306a36Sopenharmony_ci		err = ubi_check_volume(ubi, vol->vol_id);
36262306a36Sopenharmony_ci		if (err < 0)
36362306a36Sopenharmony_ci			return err;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		if (err) {
36662306a36Sopenharmony_ci			ubi_warn(ubi, "volume %d on UBI device %d is corrupted",
36762306a36Sopenharmony_ci				 vol->vol_id, ubi->ubi_num);
36862306a36Sopenharmony_ci			vol->corrupted = 1;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci		vol->checked = 1;
37162306a36Sopenharmony_ci		ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED);
37262306a36Sopenharmony_ci		revoke_exclusive(desc, UBI_READWRITE);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	return count;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic long vol_cdev_ioctl(struct file *file, unsigned int cmd,
37962306a36Sopenharmony_ci			   unsigned long arg)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int err = 0;
38262306a36Sopenharmony_ci	struct ubi_volume_desc *desc = file->private_data;
38362306a36Sopenharmony_ci	struct ubi_volume *vol = desc->vol;
38462306a36Sopenharmony_ci	struct ubi_device *ubi = vol->ubi;
38562306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	switch (cmd) {
38862306a36Sopenharmony_ci	/* Volume update command */
38962306a36Sopenharmony_ci	case UBI_IOCVOLUP:
39062306a36Sopenharmony_ci	{
39162306a36Sopenharmony_ci		int64_t bytes, rsvd_bytes;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		if (!capable(CAP_SYS_RESOURCE)) {
39462306a36Sopenharmony_ci			err = -EPERM;
39562306a36Sopenharmony_ci			break;
39662306a36Sopenharmony_ci		}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		err = copy_from_user(&bytes, argp, sizeof(int64_t));
39962306a36Sopenharmony_ci		if (err) {
40062306a36Sopenharmony_ci			err = -EFAULT;
40162306a36Sopenharmony_ci			break;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (desc->mode == UBI_READONLY) {
40562306a36Sopenharmony_ci			err = -EROFS;
40662306a36Sopenharmony_ci			break;
40762306a36Sopenharmony_ci		}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		rsvd_bytes = (long long)vol->reserved_pebs *
41062306a36Sopenharmony_ci					vol->usable_leb_size;
41162306a36Sopenharmony_ci		if (bytes < 0 || bytes > rsvd_bytes) {
41262306a36Sopenharmony_ci			err = -EINVAL;
41362306a36Sopenharmony_ci			break;
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		err = get_exclusive(desc);
41762306a36Sopenharmony_ci		if (err < 0)
41862306a36Sopenharmony_ci			break;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		err = ubi_start_update(ubi, vol, bytes);
42162306a36Sopenharmony_ci		if (bytes == 0) {
42262306a36Sopenharmony_ci			ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED);
42362306a36Sopenharmony_ci			revoke_exclusive(desc, UBI_READWRITE);
42462306a36Sopenharmony_ci		}
42562306a36Sopenharmony_ci		break;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* Atomic logical eraseblock change command */
42962306a36Sopenharmony_ci	case UBI_IOCEBCH:
43062306a36Sopenharmony_ci	{
43162306a36Sopenharmony_ci		struct ubi_leb_change_req req;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		err = copy_from_user(&req, argp,
43462306a36Sopenharmony_ci				     sizeof(struct ubi_leb_change_req));
43562306a36Sopenharmony_ci		if (err) {
43662306a36Sopenharmony_ci			err = -EFAULT;
43762306a36Sopenharmony_ci			break;
43862306a36Sopenharmony_ci		}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		if (desc->mode == UBI_READONLY ||
44162306a36Sopenharmony_ci		    vol->vol_type == UBI_STATIC_VOLUME) {
44262306a36Sopenharmony_ci			err = -EROFS;
44362306a36Sopenharmony_ci			break;
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		/* Validate the request */
44762306a36Sopenharmony_ci		err = -EINVAL;
44862306a36Sopenharmony_ci		if (!ubi_leb_valid(vol, req.lnum) ||
44962306a36Sopenharmony_ci		    req.bytes < 0 || req.bytes > vol->usable_leb_size)
45062306a36Sopenharmony_ci			break;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		err = get_exclusive(desc);
45362306a36Sopenharmony_ci		if (err < 0)
45462306a36Sopenharmony_ci			break;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		err = ubi_start_leb_change(ubi, vol, &req);
45762306a36Sopenharmony_ci		if (req.bytes == 0)
45862306a36Sopenharmony_ci			revoke_exclusive(desc, UBI_READWRITE);
45962306a36Sopenharmony_ci		break;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Logical eraseblock erasure command */
46362306a36Sopenharmony_ci	case UBI_IOCEBER:
46462306a36Sopenharmony_ci	{
46562306a36Sopenharmony_ci		int32_t lnum;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		err = get_user(lnum, (__user int32_t *)argp);
46862306a36Sopenharmony_ci		if (err) {
46962306a36Sopenharmony_ci			err = -EFAULT;
47062306a36Sopenharmony_ci			break;
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		if (desc->mode == UBI_READONLY ||
47462306a36Sopenharmony_ci		    vol->vol_type == UBI_STATIC_VOLUME) {
47562306a36Sopenharmony_ci			err = -EROFS;
47662306a36Sopenharmony_ci			break;
47762306a36Sopenharmony_ci		}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		if (!ubi_leb_valid(vol, lnum)) {
48062306a36Sopenharmony_ci			err = -EINVAL;
48162306a36Sopenharmony_ci			break;
48262306a36Sopenharmony_ci		}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		dbg_gen("erase LEB %d:%d", vol->vol_id, lnum);
48562306a36Sopenharmony_ci		err = ubi_eba_unmap_leb(ubi, vol, lnum);
48662306a36Sopenharmony_ci		if (err)
48762306a36Sopenharmony_ci			break;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Logical eraseblock map command */
49462306a36Sopenharmony_ci	case UBI_IOCEBMAP:
49562306a36Sopenharmony_ci	{
49662306a36Sopenharmony_ci		struct ubi_map_req req;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		err = copy_from_user(&req, argp, sizeof(struct ubi_map_req));
49962306a36Sopenharmony_ci		if (err) {
50062306a36Sopenharmony_ci			err = -EFAULT;
50162306a36Sopenharmony_ci			break;
50262306a36Sopenharmony_ci		}
50362306a36Sopenharmony_ci		err = ubi_leb_map(desc, req.lnum);
50462306a36Sopenharmony_ci		break;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/* Logical eraseblock un-map command */
50862306a36Sopenharmony_ci	case UBI_IOCEBUNMAP:
50962306a36Sopenharmony_ci	{
51062306a36Sopenharmony_ci		int32_t lnum;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		err = get_user(lnum, (__user int32_t *)argp);
51362306a36Sopenharmony_ci		if (err) {
51462306a36Sopenharmony_ci			err = -EFAULT;
51562306a36Sopenharmony_ci			break;
51662306a36Sopenharmony_ci		}
51762306a36Sopenharmony_ci		err = ubi_leb_unmap(desc, lnum);
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* Check if logical eraseblock is mapped command */
52262306a36Sopenharmony_ci	case UBI_IOCEBISMAP:
52362306a36Sopenharmony_ci	{
52462306a36Sopenharmony_ci		int32_t lnum;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		err = get_user(lnum, (__user int32_t *)argp);
52762306a36Sopenharmony_ci		if (err) {
52862306a36Sopenharmony_ci			err = -EFAULT;
52962306a36Sopenharmony_ci			break;
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci		err = ubi_is_mapped(desc, lnum);
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Set volume property command */
53662306a36Sopenharmony_ci	case UBI_IOCSETVOLPROP:
53762306a36Sopenharmony_ci	{
53862306a36Sopenharmony_ci		struct ubi_set_vol_prop_req req;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		err = copy_from_user(&req, argp,
54162306a36Sopenharmony_ci				     sizeof(struct ubi_set_vol_prop_req));
54262306a36Sopenharmony_ci		if (err) {
54362306a36Sopenharmony_ci			err = -EFAULT;
54462306a36Sopenharmony_ci			break;
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci		switch (req.property) {
54762306a36Sopenharmony_ci		case UBI_VOL_PROP_DIRECT_WRITE:
54862306a36Sopenharmony_ci			mutex_lock(&ubi->device_mutex);
54962306a36Sopenharmony_ci			desc->vol->direct_writes = !!req.value;
55062306a36Sopenharmony_ci			mutex_unlock(&ubi->device_mutex);
55162306a36Sopenharmony_ci			break;
55262306a36Sopenharmony_ci		default:
55362306a36Sopenharmony_ci			err = -EINVAL;
55462306a36Sopenharmony_ci			break;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci		break;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* Create a R/O block device on top of the UBI volume */
56062306a36Sopenharmony_ci	case UBI_IOCVOLCRBLK:
56162306a36Sopenharmony_ci	{
56262306a36Sopenharmony_ci		struct ubi_volume_info vi;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		ubi_get_volume_info(desc, &vi);
56562306a36Sopenharmony_ci		err = ubiblock_create(&vi);
56662306a36Sopenharmony_ci		break;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* Remove the R/O block device */
57062306a36Sopenharmony_ci	case UBI_IOCVOLRMBLK:
57162306a36Sopenharmony_ci	{
57262306a36Sopenharmony_ci		struct ubi_volume_info vi;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		ubi_get_volume_info(desc, &vi);
57562306a36Sopenharmony_ci		err = ubiblock_remove(&vi);
57662306a36Sopenharmony_ci		break;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	default:
58062306a36Sopenharmony_ci		err = -ENOTTY;
58162306a36Sopenharmony_ci		break;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	return err;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/**
58762306a36Sopenharmony_ci * verify_mkvol_req - verify volume creation request.
58862306a36Sopenharmony_ci * @ubi: UBI device description object
58962306a36Sopenharmony_ci * @req: the request to check
59062306a36Sopenharmony_ci *
59162306a36Sopenharmony_ci * This function zero if the request is correct, and %-EINVAL if not.
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_cistatic int verify_mkvol_req(const struct ubi_device *ubi,
59462306a36Sopenharmony_ci			    const struct ubi_mkvol_req *req)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	int n, err = -EINVAL;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 ||
59962306a36Sopenharmony_ci	    req->name_len < 0)
60062306a36Sopenharmony_ci		goto bad;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) &&
60362306a36Sopenharmony_ci	    req->vol_id != UBI_VOL_NUM_AUTO)
60462306a36Sopenharmony_ci		goto bad;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (req->alignment == 0)
60762306a36Sopenharmony_ci		goto bad;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (req->bytes == 0)
61062306a36Sopenharmony_ci		goto bad;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (req->vol_type != UBI_DYNAMIC_VOLUME &&
61362306a36Sopenharmony_ci	    req->vol_type != UBI_STATIC_VOLUME)
61462306a36Sopenharmony_ci		goto bad;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (req->flags & ~UBI_VOL_VALID_FLGS)
61762306a36Sopenharmony_ci		goto bad;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (req->flags & UBI_VOL_SKIP_CRC_CHECK_FLG &&
62062306a36Sopenharmony_ci	    req->vol_type != UBI_STATIC_VOLUME)
62162306a36Sopenharmony_ci		goto bad;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (req->alignment > ubi->leb_size)
62462306a36Sopenharmony_ci		goto bad;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	n = req->alignment & (ubi->min_io_size - 1);
62762306a36Sopenharmony_ci	if (req->alignment != 1 && n)
62862306a36Sopenharmony_ci		goto bad;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (!req->name[0] || !req->name_len)
63162306a36Sopenharmony_ci		goto bad;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (req->name_len > UBI_VOL_NAME_MAX) {
63462306a36Sopenharmony_ci		err = -ENAMETOOLONG;
63562306a36Sopenharmony_ci		goto bad;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	n = strnlen(req->name, req->name_len + 1);
63962306a36Sopenharmony_ci	if (n != req->name_len)
64062306a36Sopenharmony_ci		goto bad;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return 0;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cibad:
64562306a36Sopenharmony_ci	ubi_err(ubi, "bad volume creation request");
64662306a36Sopenharmony_ci	ubi_dump_mkvol_req(req);
64762306a36Sopenharmony_ci	return err;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/**
65162306a36Sopenharmony_ci * verify_rsvol_req - verify volume re-size request.
65262306a36Sopenharmony_ci * @ubi: UBI device description object
65362306a36Sopenharmony_ci * @req: the request to check
65462306a36Sopenharmony_ci *
65562306a36Sopenharmony_ci * This function returns zero if the request is correct, and %-EINVAL if not.
65662306a36Sopenharmony_ci */
65762306a36Sopenharmony_cistatic int verify_rsvol_req(const struct ubi_device *ubi,
65862306a36Sopenharmony_ci			    const struct ubi_rsvol_req *req)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	if (req->bytes <= 0)
66162306a36Sopenharmony_ci		return -EINVAL;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots)
66462306a36Sopenharmony_ci		return -EINVAL;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	return 0;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci/**
67062306a36Sopenharmony_ci * rename_volumes - rename UBI volumes.
67162306a36Sopenharmony_ci * @ubi: UBI device description object
67262306a36Sopenharmony_ci * @req: volumes re-name request
67362306a36Sopenharmony_ci *
67462306a36Sopenharmony_ci * This is a helper function for the volume re-name IOCTL which validates the
67562306a36Sopenharmony_ci * request, opens the volume and calls corresponding volumes management
67662306a36Sopenharmony_ci * function. Returns zero in case of success and a negative error code in case
67762306a36Sopenharmony_ci * of failure.
67862306a36Sopenharmony_ci */
67962306a36Sopenharmony_cistatic int rename_volumes(struct ubi_device *ubi,
68062306a36Sopenharmony_ci			  struct ubi_rnvol_req *req)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	int i, n, err;
68362306a36Sopenharmony_ci	struct list_head rename_list;
68462306a36Sopenharmony_ci	struct ubi_rename_entry *re, *re1;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (req->count < 0 || req->count > UBI_MAX_RNVOL)
68762306a36Sopenharmony_ci		return -EINVAL;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (req->count == 0)
69062306a36Sopenharmony_ci		return 0;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Validate volume IDs and names in the request */
69362306a36Sopenharmony_ci	for (i = 0; i < req->count; i++) {
69462306a36Sopenharmony_ci		if (req->ents[i].vol_id < 0 ||
69562306a36Sopenharmony_ci		    req->ents[i].vol_id >= ubi->vtbl_slots)
69662306a36Sopenharmony_ci			return -EINVAL;
69762306a36Sopenharmony_ci		if (req->ents[i].name_len < 0)
69862306a36Sopenharmony_ci			return -EINVAL;
69962306a36Sopenharmony_ci		if (req->ents[i].name_len > UBI_VOL_NAME_MAX)
70062306a36Sopenharmony_ci			return -ENAMETOOLONG;
70162306a36Sopenharmony_ci		req->ents[i].name[req->ents[i].name_len] = '\0';
70262306a36Sopenharmony_ci		n = strlen(req->ents[i].name);
70362306a36Sopenharmony_ci		if (n != req->ents[i].name_len)
70462306a36Sopenharmony_ci			return -EINVAL;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* Make sure volume IDs and names are unique */
70862306a36Sopenharmony_ci	for (i = 0; i < req->count - 1; i++) {
70962306a36Sopenharmony_ci		for (n = i + 1; n < req->count; n++) {
71062306a36Sopenharmony_ci			if (req->ents[i].vol_id == req->ents[n].vol_id) {
71162306a36Sopenharmony_ci				ubi_err(ubi, "duplicated volume id %d",
71262306a36Sopenharmony_ci					req->ents[i].vol_id);
71362306a36Sopenharmony_ci				return -EINVAL;
71462306a36Sopenharmony_ci			}
71562306a36Sopenharmony_ci			if (!strcmp(req->ents[i].name, req->ents[n].name)) {
71662306a36Sopenharmony_ci				ubi_err(ubi, "duplicated volume name \"%s\"",
71762306a36Sopenharmony_ci					req->ents[i].name);
71862306a36Sopenharmony_ci				return -EINVAL;
71962306a36Sopenharmony_ci			}
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* Create the re-name list */
72462306a36Sopenharmony_ci	INIT_LIST_HEAD(&rename_list);
72562306a36Sopenharmony_ci	for (i = 0; i < req->count; i++) {
72662306a36Sopenharmony_ci		int vol_id = req->ents[i].vol_id;
72762306a36Sopenharmony_ci		int name_len = req->ents[i].name_len;
72862306a36Sopenharmony_ci		const char *name = req->ents[i].name;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
73162306a36Sopenharmony_ci		if (!re) {
73262306a36Sopenharmony_ci			err = -ENOMEM;
73362306a36Sopenharmony_ci			goto out_free;
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		re->desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_METAONLY);
73762306a36Sopenharmony_ci		if (IS_ERR(re->desc)) {
73862306a36Sopenharmony_ci			err = PTR_ERR(re->desc);
73962306a36Sopenharmony_ci			ubi_err(ubi, "cannot open volume %d, error %d",
74062306a36Sopenharmony_ci				vol_id, err);
74162306a36Sopenharmony_ci			kfree(re);
74262306a36Sopenharmony_ci			goto out_free;
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci		/* Skip this re-naming if the name does not really change */
74662306a36Sopenharmony_ci		if (re->desc->vol->name_len == name_len &&
74762306a36Sopenharmony_ci		    !memcmp(re->desc->vol->name, name, name_len)) {
74862306a36Sopenharmony_ci			ubi_close_volume(re->desc);
74962306a36Sopenharmony_ci			kfree(re);
75062306a36Sopenharmony_ci			continue;
75162306a36Sopenharmony_ci		}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci		re->new_name_len = name_len;
75462306a36Sopenharmony_ci		memcpy(re->new_name, name, name_len);
75562306a36Sopenharmony_ci		list_add_tail(&re->list, &rename_list);
75662306a36Sopenharmony_ci		dbg_gen("will rename volume %d from \"%s\" to \"%s\"",
75762306a36Sopenharmony_ci			vol_id, re->desc->vol->name, name);
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	if (list_empty(&rename_list))
76162306a36Sopenharmony_ci		return 0;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* Find out the volumes which have to be removed */
76462306a36Sopenharmony_ci	list_for_each_entry(re, &rename_list, list) {
76562306a36Sopenharmony_ci		struct ubi_volume_desc *desc;
76662306a36Sopenharmony_ci		int no_remove_needed = 0;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		/*
76962306a36Sopenharmony_ci		 * Volume @re->vol_id is going to be re-named to
77062306a36Sopenharmony_ci		 * @re->new_name, while its current name is @name. If a volume
77162306a36Sopenharmony_ci		 * with name @re->new_name currently exists, it has to be
77262306a36Sopenharmony_ci		 * removed, unless it is also re-named in the request (@req).
77362306a36Sopenharmony_ci		 */
77462306a36Sopenharmony_ci		list_for_each_entry(re1, &rename_list, list) {
77562306a36Sopenharmony_ci			if (re->new_name_len == re1->desc->vol->name_len &&
77662306a36Sopenharmony_ci			    !memcmp(re->new_name, re1->desc->vol->name,
77762306a36Sopenharmony_ci				    re1->desc->vol->name_len)) {
77862306a36Sopenharmony_ci				no_remove_needed = 1;
77962306a36Sopenharmony_ci				break;
78062306a36Sopenharmony_ci			}
78162306a36Sopenharmony_ci		}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci		if (no_remove_needed)
78462306a36Sopenharmony_ci			continue;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		/*
78762306a36Sopenharmony_ci		 * It seems we need to remove volume with name @re->new_name,
78862306a36Sopenharmony_ci		 * if it exists.
78962306a36Sopenharmony_ci		 */
79062306a36Sopenharmony_ci		desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name,
79162306a36Sopenharmony_ci					  UBI_EXCLUSIVE);
79262306a36Sopenharmony_ci		if (IS_ERR(desc)) {
79362306a36Sopenharmony_ci			err = PTR_ERR(desc);
79462306a36Sopenharmony_ci			if (err == -ENODEV)
79562306a36Sopenharmony_ci				/* Re-naming into a non-existing volume name */
79662306a36Sopenharmony_ci				continue;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci			/* The volume exists but busy, or an error occurred */
79962306a36Sopenharmony_ci			ubi_err(ubi, "cannot open volume \"%s\", error %d",
80062306a36Sopenharmony_ci				re->new_name, err);
80162306a36Sopenharmony_ci			goto out_free;
80262306a36Sopenharmony_ci		}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		re1 = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL);
80562306a36Sopenharmony_ci		if (!re1) {
80662306a36Sopenharmony_ci			err = -ENOMEM;
80762306a36Sopenharmony_ci			ubi_close_volume(desc);
80862306a36Sopenharmony_ci			goto out_free;
80962306a36Sopenharmony_ci		}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		re1->remove = 1;
81262306a36Sopenharmony_ci		re1->desc = desc;
81362306a36Sopenharmony_ci		list_add(&re1->list, &rename_list);
81462306a36Sopenharmony_ci		dbg_gen("will remove volume %d, name \"%s\"",
81562306a36Sopenharmony_ci			re1->desc->vol->vol_id, re1->desc->vol->name);
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	mutex_lock(&ubi->device_mutex);
81962306a36Sopenharmony_ci	err = ubi_rename_volumes(ubi, &rename_list);
82062306a36Sopenharmony_ci	mutex_unlock(&ubi->device_mutex);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ciout_free:
82362306a36Sopenharmony_ci	list_for_each_entry_safe(re, re1, &rename_list, list) {
82462306a36Sopenharmony_ci		ubi_close_volume(re->desc);
82562306a36Sopenharmony_ci		list_del(&re->list);
82662306a36Sopenharmony_ci		kfree(re);
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci	return err;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic long ubi_cdev_ioctl(struct file *file, unsigned int cmd,
83262306a36Sopenharmony_ci			   unsigned long arg)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	int err = 0;
83562306a36Sopenharmony_ci	struct ubi_device *ubi;
83662306a36Sopenharmony_ci	struct ubi_volume_desc *desc;
83762306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (!capable(CAP_SYS_RESOURCE))
84062306a36Sopenharmony_ci		return -EPERM;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ubi = ubi_get_by_major(imajor(file->f_mapping->host));
84362306a36Sopenharmony_ci	if (!ubi)
84462306a36Sopenharmony_ci		return -ENODEV;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	switch (cmd) {
84762306a36Sopenharmony_ci	/* Create volume command */
84862306a36Sopenharmony_ci	case UBI_IOCMKVOL:
84962306a36Sopenharmony_ci	{
85062306a36Sopenharmony_ci		struct ubi_mkvol_req req;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		dbg_gen("create volume");
85362306a36Sopenharmony_ci		err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req));
85462306a36Sopenharmony_ci		if (err) {
85562306a36Sopenharmony_ci			err = -EFAULT;
85662306a36Sopenharmony_ci			break;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		err = verify_mkvol_req(ubi, &req);
86062306a36Sopenharmony_ci		if (err)
86162306a36Sopenharmony_ci			break;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		mutex_lock(&ubi->device_mutex);
86462306a36Sopenharmony_ci		err = ubi_create_volume(ubi, &req);
86562306a36Sopenharmony_ci		mutex_unlock(&ubi->device_mutex);
86662306a36Sopenharmony_ci		if (err)
86762306a36Sopenharmony_ci			break;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		err = put_user(req.vol_id, (__user int32_t *)argp);
87062306a36Sopenharmony_ci		if (err)
87162306a36Sopenharmony_ci			err = -EFAULT;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci		break;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/* Remove volume command */
87762306a36Sopenharmony_ci	case UBI_IOCRMVOL:
87862306a36Sopenharmony_ci	{
87962306a36Sopenharmony_ci		int vol_id;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		dbg_gen("remove volume");
88262306a36Sopenharmony_ci		err = get_user(vol_id, (__user int32_t *)argp);
88362306a36Sopenharmony_ci		if (err) {
88462306a36Sopenharmony_ci			err = -EFAULT;
88562306a36Sopenharmony_ci			break;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci		desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE);
88962306a36Sopenharmony_ci		if (IS_ERR(desc)) {
89062306a36Sopenharmony_ci			err = PTR_ERR(desc);
89162306a36Sopenharmony_ci			break;
89262306a36Sopenharmony_ci		}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		mutex_lock(&ubi->device_mutex);
89562306a36Sopenharmony_ci		err = ubi_remove_volume(desc, 0);
89662306a36Sopenharmony_ci		mutex_unlock(&ubi->device_mutex);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci		/*
89962306a36Sopenharmony_ci		 * The volume is deleted (unless an error occurred), and the
90062306a36Sopenharmony_ci		 * 'struct ubi_volume' object will be freed when
90162306a36Sopenharmony_ci		 * 'ubi_close_volume()' will call 'put_device()'.
90262306a36Sopenharmony_ci		 */
90362306a36Sopenharmony_ci		ubi_close_volume(desc);
90462306a36Sopenharmony_ci		break;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* Re-size volume command */
90862306a36Sopenharmony_ci	case UBI_IOCRSVOL:
90962306a36Sopenharmony_ci	{
91062306a36Sopenharmony_ci		int pebs;
91162306a36Sopenharmony_ci		struct ubi_rsvol_req req;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		dbg_gen("re-size volume");
91462306a36Sopenharmony_ci		err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req));
91562306a36Sopenharmony_ci		if (err) {
91662306a36Sopenharmony_ci			err = -EFAULT;
91762306a36Sopenharmony_ci			break;
91862306a36Sopenharmony_ci		}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		err = verify_rsvol_req(ubi, &req);
92162306a36Sopenharmony_ci		if (err)
92262306a36Sopenharmony_ci			break;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		desc = ubi_open_volume(ubi->ubi_num, req.vol_id, UBI_EXCLUSIVE);
92562306a36Sopenharmony_ci		if (IS_ERR(desc)) {
92662306a36Sopenharmony_ci			err = PTR_ERR(desc);
92762306a36Sopenharmony_ci			break;
92862306a36Sopenharmony_ci		}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		pebs = div_u64(req.bytes + desc->vol->usable_leb_size - 1,
93162306a36Sopenharmony_ci			       desc->vol->usable_leb_size);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		mutex_lock(&ubi->device_mutex);
93462306a36Sopenharmony_ci		err = ubi_resize_volume(desc, pebs);
93562306a36Sopenharmony_ci		mutex_unlock(&ubi->device_mutex);
93662306a36Sopenharmony_ci		ubi_close_volume(desc);
93762306a36Sopenharmony_ci		break;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* Re-name volumes command */
94162306a36Sopenharmony_ci	case UBI_IOCRNVOL:
94262306a36Sopenharmony_ci	{
94362306a36Sopenharmony_ci		struct ubi_rnvol_req *req;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		dbg_gen("re-name volumes");
94662306a36Sopenharmony_ci		req = kmalloc(sizeof(struct ubi_rnvol_req), GFP_KERNEL);
94762306a36Sopenharmony_ci		if (!req) {
94862306a36Sopenharmony_ci			err = -ENOMEM;
94962306a36Sopenharmony_ci			break;
95062306a36Sopenharmony_ci		}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci		err = copy_from_user(req, argp, sizeof(struct ubi_rnvol_req));
95362306a36Sopenharmony_ci		if (err) {
95462306a36Sopenharmony_ci			err = -EFAULT;
95562306a36Sopenharmony_ci			kfree(req);
95662306a36Sopenharmony_ci			break;
95762306a36Sopenharmony_ci		}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci		err = rename_volumes(ubi, req);
96062306a36Sopenharmony_ci		kfree(req);
96162306a36Sopenharmony_ci		break;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* Check a specific PEB for bitflips and scrub it if needed */
96562306a36Sopenharmony_ci	case UBI_IOCRPEB:
96662306a36Sopenharmony_ci	{
96762306a36Sopenharmony_ci		int pnum;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		err = get_user(pnum, (__user int32_t *)argp);
97062306a36Sopenharmony_ci		if (err) {
97162306a36Sopenharmony_ci			err = -EFAULT;
97262306a36Sopenharmony_ci			break;
97362306a36Sopenharmony_ci		}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		err = ubi_bitflip_check(ubi, pnum, 0);
97662306a36Sopenharmony_ci		break;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* Force scrubbing for a specific PEB */
98062306a36Sopenharmony_ci	case UBI_IOCSPEB:
98162306a36Sopenharmony_ci	{
98262306a36Sopenharmony_ci		int pnum;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		err = get_user(pnum, (__user int32_t *)argp);
98562306a36Sopenharmony_ci		if (err) {
98662306a36Sopenharmony_ci			err = -EFAULT;
98762306a36Sopenharmony_ci			break;
98862306a36Sopenharmony_ci		}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		err = ubi_bitflip_check(ubi, pnum, 1);
99162306a36Sopenharmony_ci		break;
99262306a36Sopenharmony_ci	}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	default:
99562306a36Sopenharmony_ci		err = -ENOTTY;
99662306a36Sopenharmony_ci		break;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	ubi_put_device(ubi);
100062306a36Sopenharmony_ci	return err;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic long ctrl_cdev_ioctl(struct file *file, unsigned int cmd,
100462306a36Sopenharmony_ci			    unsigned long arg)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	int err = 0;
100762306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (!capable(CAP_SYS_RESOURCE))
101062306a36Sopenharmony_ci		return -EPERM;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	switch (cmd) {
101362306a36Sopenharmony_ci	/* Attach an MTD device command */
101462306a36Sopenharmony_ci	case UBI_IOCATT:
101562306a36Sopenharmony_ci	{
101662306a36Sopenharmony_ci		struct ubi_attach_req req;
101762306a36Sopenharmony_ci		struct mtd_info *mtd;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci		dbg_gen("attach MTD device");
102062306a36Sopenharmony_ci		err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req));
102162306a36Sopenharmony_ci		if (err) {
102262306a36Sopenharmony_ci			err = -EFAULT;
102362306a36Sopenharmony_ci			break;
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		if (req.mtd_num < 0 ||
102762306a36Sopenharmony_ci		    (req.ubi_num < 0 && req.ubi_num != UBI_DEV_NUM_AUTO)) {
102862306a36Sopenharmony_ci			err = -EINVAL;
102962306a36Sopenharmony_ci			break;
103062306a36Sopenharmony_ci		}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		mtd = get_mtd_device(NULL, req.mtd_num);
103362306a36Sopenharmony_ci		if (IS_ERR(mtd)) {
103462306a36Sopenharmony_ci			err = PTR_ERR(mtd);
103562306a36Sopenharmony_ci			break;
103662306a36Sopenharmony_ci		}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		/*
103962306a36Sopenharmony_ci		 * Note, further request verification is done by
104062306a36Sopenharmony_ci		 * 'ubi_attach_mtd_dev()'.
104162306a36Sopenharmony_ci		 */
104262306a36Sopenharmony_ci		mutex_lock(&ubi_devices_mutex);
104362306a36Sopenharmony_ci		err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset,
104462306a36Sopenharmony_ci					 req.max_beb_per1024, !!req.disable_fm);
104562306a36Sopenharmony_ci		mutex_unlock(&ubi_devices_mutex);
104662306a36Sopenharmony_ci		if (err < 0)
104762306a36Sopenharmony_ci			put_mtd_device(mtd);
104862306a36Sopenharmony_ci		else
104962306a36Sopenharmony_ci			/* @err contains UBI device number */
105062306a36Sopenharmony_ci			err = put_user(err, (__user int32_t *)argp);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		break;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	/* Detach an MTD device command */
105662306a36Sopenharmony_ci	case UBI_IOCDET:
105762306a36Sopenharmony_ci	{
105862306a36Sopenharmony_ci		int ubi_num;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		dbg_gen("detach MTD device");
106162306a36Sopenharmony_ci		err = get_user(ubi_num, (__user int32_t *)argp);
106262306a36Sopenharmony_ci		if (err) {
106362306a36Sopenharmony_ci			err = -EFAULT;
106462306a36Sopenharmony_ci			break;
106562306a36Sopenharmony_ci		}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		mutex_lock(&ubi_devices_mutex);
106862306a36Sopenharmony_ci		err = ubi_detach_mtd_dev(ubi_num, 0);
106962306a36Sopenharmony_ci		mutex_unlock(&ubi_devices_mutex);
107062306a36Sopenharmony_ci		break;
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	default:
107462306a36Sopenharmony_ci		err = -ENOTTY;
107562306a36Sopenharmony_ci		break;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	return err;
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci/* UBI volume character device operations */
108262306a36Sopenharmony_ciconst struct file_operations ubi_vol_cdev_operations = {
108362306a36Sopenharmony_ci	.owner          = THIS_MODULE,
108462306a36Sopenharmony_ci	.open           = vol_cdev_open,
108562306a36Sopenharmony_ci	.release        = vol_cdev_release,
108662306a36Sopenharmony_ci	.llseek         = vol_cdev_llseek,
108762306a36Sopenharmony_ci	.read           = vol_cdev_read,
108862306a36Sopenharmony_ci	.write          = vol_cdev_write,
108962306a36Sopenharmony_ci	.fsync		= vol_cdev_fsync,
109062306a36Sopenharmony_ci	.unlocked_ioctl = vol_cdev_ioctl,
109162306a36Sopenharmony_ci	.compat_ioctl   = compat_ptr_ioctl,
109262306a36Sopenharmony_ci};
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci/* UBI character device operations */
109562306a36Sopenharmony_ciconst struct file_operations ubi_cdev_operations = {
109662306a36Sopenharmony_ci	.owner          = THIS_MODULE,
109762306a36Sopenharmony_ci	.llseek         = no_llseek,
109862306a36Sopenharmony_ci	.unlocked_ioctl = ubi_cdev_ioctl,
109962306a36Sopenharmony_ci	.compat_ioctl   = compat_ptr_ioctl,
110062306a36Sopenharmony_ci};
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci/* UBI control character device operations */
110362306a36Sopenharmony_ciconst struct file_operations ubi_ctrl_cdev_operations = {
110462306a36Sopenharmony_ci	.owner          = THIS_MODULE,
110562306a36Sopenharmony_ci	.unlocked_ioctl = ctrl_cdev_ioctl,
110662306a36Sopenharmony_ci	.compat_ioctl   = compat_ptr_ioctl,
110762306a36Sopenharmony_ci	.llseek		= no_llseek,
110862306a36Sopenharmony_ci};
1109