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