// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) International Business Machines Corp., 2006 * * Author: Artem Bityutskiy (Битюцкий Артём)
*/
/* * This file includes implementation of UBI character device operations. * * There are two kinds of character devices in UBI: UBI character devices and * UBI volume character devices. UBI character devices allow users to * manipulate whole volumes: create, remove, and re-size them. Volume character * devices provide volume I/O capabilities. * * Major and minor numbers are assigned dynamically to both UBI and volume * character devices. * * Well, there is the third kind of character devices - the UBI control * character device, which allows to manipulate by UBI devices - create and * delete them. In other words, it is used for attaching and detaching MTD * devices.
*/
/** * get_exclusive - get exclusive access to an UBI volume. * @desc: volume descriptor * * This function changes UBI volume open mode to "exclusive". Returns previous * mode value (positive integer) in case of success and a negative error code * in case of failure.
*/ staticint get_exclusive(struct ubi_volume_desc *desc)
{ int users, err; struct ubi_volume *vol = desc->vol;
/* We can write only in fractions of the minimum I/O unit */ if (count & (ubi->min_io_size - 1)) {
ubi_err(ubi, "unaligned write length"); return -EINVAL;
}
tbuf_size = vol->usable_leb_size; if (count < tbuf_size)
tbuf_size = ALIGN(count, ubi->min_io_size);
tbuf = vmalloc(tbuf_size); if (!tbuf) return -ENOMEM;
len = count > tbuf_size ? tbuf_size : count;
while (count) {
cond_resched();
if (off + len >= vol->usable_leb_size)
len = vol->usable_leb_size - off;
if (err < 0) {
ubi_err(ubi, "cannot accept more %zd bytes of data, error %d",
count, err); return err;
}
if (err) { /* * The operation is finished, @err contains number of actually * written bytes.
*/
count = err;
if (vol->changing_leb) {
revoke_exclusive(desc, UBI_READWRITE); return count;
}
/* * We voluntarily do not take into account the skip_check flag * as we want to make sure what we wrote was correctly written.
*/
err = ubi_check_volume(ubi, vol->vol_id); if (err < 0) return err;
if (err) {
ubi_warn(ubi, "volume %d on UBI device %d is corrupted",
vol->vol_id, ubi->ubi_num);
vol->corrupted = 1;
}
vol->checked = 1;
ubi_volume_notify(ubi, vol, UBI_VOLUME_UPDATED);
revoke_exclusive(desc, UBI_READWRITE);
}
/** * verify_mkvol_req - verify volume creation request. * @ubi: UBI device description object * @req: the request to check * * This function zero if the request is correct, and %-EINVAL if not.
*/ staticint verify_mkvol_req(conststruct ubi_device *ubi, conststruct ubi_mkvol_req *req)
{ int n, err = -EINVAL;
/** * verify_rsvol_req - verify volume re-size request. * @ubi: UBI device description object * @req: the request to check * * This function returns zero if the request is correct, and %-EINVAL if not.
*/ staticint verify_rsvol_req(conststruct ubi_device *ubi, conststruct ubi_rsvol_req *req)
{ if (req->bytes <= 0) return -EINVAL;
if (req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) return -EINVAL;
return 0;
}
/** * rename_volumes - rename UBI volumes. * @ubi: UBI device description object * @req: volumes re-name request * * This is a helper function for the volume re-name IOCTL which validates the * request, opens the volume and calls corresponding volumes management * function. Returns zero in case of success and a negative error code in case * of failure.
*/ staticint rename_volumes(struct ubi_device *ubi, struct ubi_rnvol_req *req)
{ int i, n, err; struct list_head rename_list; struct ubi_rename_entry *re, *re1;
if (req->count < 0 || req->count > UBI_MAX_RNVOL) return -EINVAL;
if (req->count == 0) return 0;
/* Validate volume IDs and names in the request */ for (i = 0; i < req->count; i++) { if (req->ents[i].vol_id < 0 ||
req->ents[i].vol_id >= ubi->vtbl_slots) return -EINVAL; if (req->ents[i].name_len < 0) return -EINVAL; if (req->ents[i].name_len > UBI_VOL_NAME_MAX) return -ENAMETOOLONG;
req->ents[i].name[req->ents[i].name_len] = '\0';
n = strlen(req->ents[i].name); if (n != req->ents[i].name_len) return -EINVAL;
}
/* Make sure volume IDs and names are unique */ for (i = 0; i < req->count - 1; i++) { for (n = i + 1; n < req->count; n++) { if (req->ents[i].vol_id == req->ents[n].vol_id) {
ubi_err(ubi, "duplicated volume id %d",
req->ents[i].vol_id); return -EINVAL;
} if (!strcmp(req->ents[i].name, req->ents[n].name)) {
ubi_err(ubi, "duplicated volume name \"%s\"",
req->ents[i].name); return -EINVAL;
}
}
}
/* Create the re-name list */
INIT_LIST_HEAD(&rename_list); for (i = 0; i < req->count; i++) { int vol_id = req->ents[i].vol_id; int name_len = req->ents[i].name_len; constchar *name = req->ents[i].name;
re = kzalloc(sizeof(struct ubi_rename_entry), GFP_KERNEL); if (!re) {
err = -ENOMEM; goto out_free;
}
/* Skip this re-naming if the name does not really change */ if (re->desc->vol->name_len == name_len &&
!memcmp(re->desc->vol->name, name, name_len)) {
ubi_close_volume(re->desc);
kfree(re); continue;
}
re->new_name_len = name_len;
memcpy(re->new_name, name, name_len);
list_add_tail(&re->list, &rename_list);
dbg_gen("will rename volume %d from \"%s\" to \"%s\"",
vol_id, re->desc->vol->name, name);
}
if (list_empty(&rename_list)) return 0;
/* Find out the volumes which have to be removed */
list_for_each_entry(re, &rename_list, list) { struct ubi_volume_desc *desc; int no_remove_needed = 0;
/* * Volume @re->vol_id is going to be re-named to * @re->new_name, while its current name is @name. If a volume * with name @re->new_name currently exists, it has to be * removed, unless it is also re-named in the request (@req).
*/
list_for_each_entry(re1, &rename_list, list) { if (re->new_name_len == re1->desc->vol->name_len &&
!memcmp(re->new_name, re1->desc->vol->name,
re1->desc->vol->name_len)) {
no_remove_needed = 1; break;
}
}
if (no_remove_needed) continue;
/* * It seems we need to remove volume with name @re->new_name, * if it exists.
*/
desc = ubi_open_volume_nm(ubi->ubi_num, re->new_name,
UBI_EXCLUSIVE); if (IS_ERR(desc)) {
err = PTR_ERR(desc); if (err == -ENODEV) /* Re-naming into a non-existing volume name */ continue;
/* The volume exists but busy, or an error occurred */
ubi_err(ubi, "cannot open volume \"%s\", error %d",
re->new_name, err); goto out_free;
}
/* * The volume is deleted (unless an error occurred), and the * 'struct ubi_volume' object will be freed when * 'ubi_close_volume()' will call 'put_device()'.
*/
ubi_close_volume(desc); break;
}
/* Re-size volume command */ case UBI_IOCRSVOL:
{ int pebs; struct ubi_rsvol_req req;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.