diff --git a/commands/ubi.c b/commands/ubi.c index 9463127..844d75d 100644 --- a/commands/ubi.c +++ b/commands/ubi.c @@ -122,14 +122,36 @@ static int do_ubidetach(int argc, char *argv[]) { - int ubi_num, ret; + int fd, ret; + struct mtd_info_user user; if (argc != 2) return COMMAND_ERROR_USAGE; - ubi_num = simple_strtoul(argv[1], NULL, 0); - ret = ubi_detach_mtd_dev(ubi_num, 1); + fd = open(argv[optind], O_RDWR); + if (fd < 0) { + int ubi_num = simple_strtoul(argv[1], NULL, 0); + ret = ubi_detach(ubi_num); + goto out; + } + ret = ioctl(fd, MEMGETINFO, &user); + if (!ret) { + int ubi_num = ubi_num_get_by_mtd(user.mtd); + if (ubi_num < 0) { + ret = ubi_num; + goto out; + } + + ret = ubi_detach(ubi_num); + if (!ret) + goto out_close; + } + +out_close: + close(fd); + +out: if (ret) printf("failed to detach: %s\n", strerror(-ret)); @@ -139,7 +161,7 @@ BAREBOX_CMD_START(ubidetach) .cmd = do_ubidetach, BAREBOX_CMD_DESC("detach an UBI device") - BAREBOX_CMD_OPTS("UBINUM") + BAREBOX_CMD_OPTS("mtd device/UBINUM") BAREBOX_CMD_GROUP(CMD_GRP_PART) BAREBOX_CMD_END diff --git a/commands/ubiformat.c b/commands/ubiformat.c index 0172654..2ffdd0c 100644 --- a/commands/ubiformat.c +++ b/commands/ubiformat.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -562,6 +563,7 @@ struct ubigen_info ui; struct ubi_scan_info *si; struct mtd_info_user mtd_user; + int ubi_num; err = parse_opt(argc, argv); if (err) @@ -622,8 +624,15 @@ goto out_close; } - /* Make sure this MTD device is not attached to UBI */ - /* FIXME! Find a proper way to do this in barebox! */ + ubi_num = ubi_num_get_by_mtd(mtd_user.mtd); + if (ubi_num >= 0) { + err = ubi_detach(ubi_num); + if (err) { + sys_errmsg("Cannot detach %d\n", err); + goto out_close; + } + } + eb_cnt = mtd_div_by_eb(mtd->size, mtd); @@ -759,6 +768,17 @@ } libscan_ubi_scan_free(si); + + /* Reattach the ubi device in case it was attached in the beginning */ + if (ubi_num >= 0) { + err = ubi_attach_mtd_dev(mtd_user.mtd, ubi_num, 0, 20); + if (err) { + pr_err("Failed to reattach ubi device to ubi number %d, %d\n", + ubi_num, err); + return err; + } + } + return 0; out_free: diff --git a/commands/umount.c b/commands/umount.c index 84c84e4..fdf4da9 100644 --- a/commands/umount.c +++ b/commands/umount.c @@ -37,7 +37,7 @@ BAREBOX_CMD_START(umount) .cmd = do_umount, BAREBOX_CMD_DESC("umount a filesystem") - BAREBOX_CMD_OPTS("MOUNTPOINT") + BAREBOX_CMD_OPTS("MOUNTPOINT/DEVICEPATH") BAREBOX_CMD_GROUP(CMD_GRP_PART) BAREBOX_CMD_HELP(cmd_umount_help) BAREBOX_CMD_END diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile index 795b116..33ac390 100644 --- a/drivers/mtd/ubi/Makefile +++ b/drivers/mtd/ubi/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_MTD_UBI) += ubi.o -ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o attach.o +ubi-y += vtbl.o vmt.o upd.o build.o barebox.o kapi.o eba.o io.o wl.o attach.o ubi-y += misc.o debug.o ubi-$(CONFIG_MTD_UBI_FASTMAP) += fastmap.o diff --git a/drivers/mtd/ubi/barebox.c b/drivers/mtd/ubi/barebox.c new file mode 100644 index 0000000..c26a245 --- /dev/null +++ b/drivers/mtd/ubi/barebox.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include "ubi-barebox.h" +#include "ubi.h" + +LIST_HEAD(ubi_volumes_list); + +struct ubi_volume_cdev_priv { + struct ubi_device *ubi; + struct ubi_volume *vol; + int written; +}; + +static ssize_t ubi_volume_cdev_read(struct cdev *cdev, void *buf, size_t size, + loff_t offset, unsigned long flags) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + struct ubi_volume *vol = priv->vol; + struct ubi_device *ubi = priv->ubi; + int err, lnum, off, len; + size_t count_save = size; + unsigned long long tmp; + loff_t offp = offset; + int usable_leb_size = vol->usable_leb_size; + + ubi_debug("%s: %zd @ 0x%08llx", __func__, size, offset); + + len = size > usable_leb_size ? usable_leb_size : size; + + tmp = offp; + off = do_div(tmp, usable_leb_size); + lnum = tmp; + do { + if (off + len >= usable_leb_size) + len = usable_leb_size - off; + + err = ubi_eba_read_leb(ubi, vol, lnum, buf, off, len, 0); + if (err) { + ubi_err("read error: %s", strerror(-err)); + break; + } + off += len; + if (off == usable_leb_size) { + lnum += 1; + off -= usable_leb_size; + } + + size -= len; + offp += len; + + buf += len; + len = size > usable_leb_size ? usable_leb_size : size; + } while (size); + + return count_save; +} + +static ssize_t ubi_volume_cdev_write(struct cdev* cdev, const void *buf, + size_t size, loff_t offset, unsigned long flags) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + struct ubi_volume *vol = priv->vol; + struct ubi_device *ubi = priv->ubi; + int err; + + if (!priv->written) { + err = ubi_start_update(ubi, vol, vol->used_bytes); + if (err < 0) { + ubi_err("Cannot start volume update"); + return err; + } + } + + err = ubi_more_update_data(ubi, vol, buf, size); + if (err < 0) { + ubi_err("Couldnt or partially wrote data"); + return err; + } + + priv->written += size; + + return size; +} + +static int ubi_volume_cdev_open(struct cdev *cdev, unsigned long flags) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + + priv->written = 0; + + return 0; +} + +static int ubi_volume_cdev_close(struct cdev *cdev) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + struct ubi_volume *vol = priv->vol; + struct ubi_device *ubi = priv->ubi; + int err; + + if (priv->written) { + int remaining = vol->usable_leb_size - + (priv->written % vol->usable_leb_size); + + if (remaining) { + void *buf = kmalloc(remaining, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + memset(buf, 0xff, remaining); + + err = ubi_more_update_data(ubi, vol, buf, remaining); + + kfree(buf); + + if (err < 0) { + ubi_err("Couldnt or partially wrote data"); + return err; + } + } + + err = ubi_finish_update(ubi, vol); + if (err) + return err; + + err = ubi_check_volume(ubi, vol->vol_id); + if (err < 0) { + ubi_err("ubi volume check failed: %s", strerror(err)); + return err; + } + + if (err) { + ubi_warn("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); + } + + return 0; +} + +static loff_t ubi_volume_cdev_lseek(struct cdev *cdev, loff_t ofs) +{ + struct ubi_volume_cdev_priv *priv = cdev->priv; + + /* We can only update ubi volumes sequentially */ + if (priv->written) + return -EINVAL; + + return ofs; +} + +static struct file_operations ubi_volume_fops = { + .open = ubi_volume_cdev_open, + .close = ubi_volume_cdev_close, + .read = ubi_volume_cdev_read, + .write = ubi_volume_cdev_write, + .lseek = ubi_volume_cdev_lseek, +}; + +int ubi_volume_cdev_add(struct ubi_device *ubi, struct ubi_volume *vol) +{ + struct cdev *cdev = &vol->cdev; + struct ubi_volume_cdev_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + + priv->vol = vol; + priv->ubi = ubi; + + cdev->ops = &ubi_volume_fops; + cdev->name = asprintf("%s.%s", ubi->cdev.name, vol->name); + cdev->priv = priv; + cdev->size = vol->used_bytes; + cdev->dev = &vol->dev; + ubi_msg("registering %s as /dev/%s", vol->name, cdev->name); + ret = devfs_create(cdev); + if (ret) { + kfree(priv); + free(cdev->name); + } + + list_add_tail(&vol->list, &ubi_volumes_list); + + return 0; +} + +void ubi_volume_cdev_remove(struct ubi_volume *vol) +{ + struct cdev *cdev = &vol->cdev; + struct ubi_volume_cdev_priv *priv = cdev->priv; + + list_del(&vol->list); + + devfs_remove(cdev); + unregister_device(&vol->dev); + kfree(cdev->name); + kfree(priv); +} + +static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf) +{ + struct ubi_volume_desc *desc; + struct ubi_device *ubi = cdev->priv; + struct ubi_mkvol_req *req = buf; + + switch (cmd) { + case UBI_IOCRMVOL: + desc = ubi_open_volume_nm(ubi->ubi_num, req->name, + UBI_EXCLUSIVE); + if (IS_ERR(desc)) + return PTR_ERR(desc); + ubi_remove_volume(desc, 0); + ubi_close_volume(desc); + break; + case UBI_IOCMKVOL: + if (!req->bytes) + req->bytes = (__s64)ubi->avail_pebs * ubi->leb_size; + return ubi_create_volume(ubi, req); + }; + + return 0; +} + +static struct file_operations ubi_fops = { + .ioctl = ubi_cdev_ioctl, +}; + +int ubi_cdev_add(struct ubi_device *ubi) +{ + struct cdev *cdev = &ubi->cdev; + int ret; + + cdev->ops = &ubi_fops; + cdev->name = asprintf("%s.ubi", ubi->mtd->cdev.name); + cdev->priv = ubi; + cdev->size = 0; + + ubi_msg("registering /dev/%s", cdev->name); + ret = devfs_create(cdev); + if (ret) + kfree(cdev->name); + + return ret; +} + +void ubi_cdev_remove(struct ubi_device *ubi) +{ + struct cdev *cdev = &ubi->cdev; + + ubi_msg("removing %s", cdev->name); + + devfs_remove(cdev); + kfree(cdev->name); +} + +static void ubi_umount_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) { + struct ubi_volume *vol = ubi->volumes[i]; + if (!vol) + continue; + umount_by_cdev(&vol->cdev); + } +} + +/** + * ubi_detach - detach an UBI device + * @ubi_num: The UBI device number + * + * UBI volumes used by UBIFS will be unmounted before detaching the + * UBI device. + * + * @return: 0 for success, negative error code otherwise + */ +int ubi_detach(int ubi_num) +{ + struct ubi_device *ubi; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + ubi = ubi_devices[ubi_num]; + if (!ubi) + return -ENOENT; + + ubi_umount_volumes(ubi); + + return ubi_detach_mtd_dev(ubi_num, 1); +} + +/** + * ubi_num_get_by_mtd - find the ubi number to the given mtd + * @mtd: the mtd device + * + * @return: positive or zero for a UBI number, negative error code otherwise + */ +int ubi_num_get_by_mtd(struct mtd_info *mtd) +{ + int i; + struct ubi_device *ubi; + + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && mtd == ubi->mtd) + return ubi->ubi_num; + } + + return -ENOENT; +} diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c deleted file mode 100644 index fe71a8d..0000000 --- a/drivers/mtd/ubi/cdev.c +++ /dev/null @@ -1,262 +0,0 @@ -#include -#include -#include -#include -#include "ubi-barebox.h" -#include "ubi.h" - -LIST_HEAD(ubi_volumes_list); - -struct ubi_volume_cdev_priv { - struct ubi_device *ubi; - struct ubi_volume *vol; - int written; -}; - -static ssize_t ubi_volume_cdev_read(struct cdev *cdev, void *buf, size_t size, - loff_t offset, unsigned long flags) -{ - struct ubi_volume_cdev_priv *priv = cdev->priv; - struct ubi_volume *vol = priv->vol; - struct ubi_device *ubi = priv->ubi; - int err, lnum, off, len; - size_t count_save = size; - unsigned long long tmp; - loff_t offp = offset; - int usable_leb_size = vol->usable_leb_size; - - ubi_debug("%s: %zd @ 0x%08llx", __func__, size, offset); - - len = size > usable_leb_size ? usable_leb_size : size; - - tmp = offp; - off = do_div(tmp, usable_leb_size); - lnum = tmp; - do { - if (off + len >= usable_leb_size) - len = usable_leb_size - off; - - err = ubi_eba_read_leb(ubi, vol, lnum, buf, off, len, 0); - if (err) { - ubi_err("read error: %s", strerror(-err)); - break; - } - off += len; - if (off == usable_leb_size) { - lnum += 1; - off -= usable_leb_size; - } - - size -= len; - offp += len; - - buf += len; - len = size > usable_leb_size ? usable_leb_size : size; - } while (size); - - return count_save; -} - -static ssize_t ubi_volume_cdev_write(struct cdev* cdev, const void *buf, - size_t size, loff_t offset, unsigned long flags) -{ - struct ubi_volume_cdev_priv *priv = cdev->priv; - struct ubi_volume *vol = priv->vol; - struct ubi_device *ubi = priv->ubi; - int err; - - if (!priv->written) { - err = ubi_start_update(ubi, vol, vol->used_bytes); - if (err < 0) { - ubi_err("Cannot start volume update"); - return err; - } - } - - err = ubi_more_update_data(ubi, vol, buf, size); - if (err < 0) { - ubi_err("Couldnt or partially wrote data"); - return err; - } - - priv->written += size; - - return size; -} - -static int ubi_volume_cdev_open(struct cdev *cdev, unsigned long flags) -{ - struct ubi_volume_cdev_priv *priv = cdev->priv; - - priv->written = 0; - - return 0; -} - -static int ubi_volume_cdev_close(struct cdev *cdev) -{ - struct ubi_volume_cdev_priv *priv = cdev->priv; - struct ubi_volume *vol = priv->vol; - struct ubi_device *ubi = priv->ubi; - int err; - - if (priv->written) { - int remaining = vol->usable_leb_size - - (priv->written % vol->usable_leb_size); - - if (remaining) { - void *buf = kmalloc(remaining, GFP_KERNEL); - - if (!buf) - return -ENOMEM; - - memset(buf, 0xff, remaining); - - err = ubi_more_update_data(ubi, vol, buf, remaining); - - kfree(buf); - - if (err < 0) { - ubi_err("Couldnt or partially wrote data"); - return err; - } - } - - err = ubi_finish_update(ubi, vol); - if (err) - return err; - - err = ubi_check_volume(ubi, vol->vol_id); - if (err < 0) { - ubi_err("ubi volume check failed: %s", strerror(err)); - return err; - } - - if (err) { - ubi_warn("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); - } - - return 0; -} - -static loff_t ubi_volume_cdev_lseek(struct cdev *cdev, loff_t ofs) -{ - struct ubi_volume_cdev_priv *priv = cdev->priv; - - /* We can only update ubi volumes sequentially */ - if (priv->written) - return -EINVAL; - - return ofs; -} - -static struct file_operations ubi_volume_fops = { - .open = ubi_volume_cdev_open, - .close = ubi_volume_cdev_close, - .read = ubi_volume_cdev_read, - .write = ubi_volume_cdev_write, - .lseek = ubi_volume_cdev_lseek, -}; - -int ubi_volume_cdev_add(struct ubi_device *ubi, struct ubi_volume *vol) -{ - struct cdev *cdev = &vol->cdev; - struct ubi_volume_cdev_priv *priv; - int ret; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - - priv->vol = vol; - priv->ubi = ubi; - - cdev->ops = &ubi_volume_fops; - cdev->name = asprintf("%s.%s", ubi->cdev.name, vol->name); - cdev->priv = priv; - cdev->size = vol->used_bytes; - cdev->dev = &vol->dev; - ubi_msg("registering %s as /dev/%s", vol->name, cdev->name); - ret = devfs_create(cdev); - if (ret) { - kfree(priv); - free(cdev->name); - } - - list_add_tail(&vol->list, &ubi_volumes_list); - - return 0; -} - -void ubi_volume_cdev_remove(struct ubi_volume *vol) -{ - struct cdev *cdev = &vol->cdev; - struct ubi_volume_cdev_priv *priv = cdev->priv; - - list_del(&vol->list); - - devfs_remove(cdev); - unregister_device(&vol->dev); - kfree(cdev->name); - kfree(priv); -} - -static int ubi_cdev_ioctl(struct cdev *cdev, int cmd, void *buf) -{ - struct ubi_volume_desc *desc; - struct ubi_device *ubi = cdev->priv; - struct ubi_mkvol_req *req = buf; - - switch (cmd) { - case UBI_IOCRMVOL: - desc = ubi_open_volume_nm(ubi->ubi_num, req->name, - UBI_EXCLUSIVE); - if (IS_ERR(desc)) - return PTR_ERR(desc); - ubi_remove_volume(desc, 0); - ubi_close_volume(desc); - break; - case UBI_IOCMKVOL: - if (!req->bytes) - req->bytes = (__s64)ubi->avail_pebs * ubi->leb_size; - return ubi_create_volume(ubi, req); - }; - - return 0; -} - -static struct file_operations ubi_fops = { - .ioctl = ubi_cdev_ioctl, -}; - -int ubi_cdev_add(struct ubi_device *ubi) -{ - struct cdev *cdev = &ubi->cdev; - int ret; - - cdev->ops = &ubi_fops; - cdev->name = asprintf("%s.ubi", ubi->mtd->cdev.name); - cdev->priv = ubi; - cdev->size = 0; - - ubi_msg("registering /dev/%s", cdev->name); - ret = devfs_create(cdev); - if (ret) - kfree(cdev->name); - - return ret; -} - -void ubi_cdev_remove(struct ubi_device *ubi) -{ - struct cdev *cdev = &ubi->cdev; - - ubi_msg("removing %s", cdev->name); - - devfs_remove(cdev); - kfree(cdev->name); -} diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 03a36d2..a8ed0d7 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -809,6 +809,7 @@ struct ubi_vid_hdr *vid_hdr); /* build.c */ +int ubi_detach_mtd_dev(int ubi_num, int anyway); struct ubi_device *ubi_get_device(int ubi_num); void ubi_put_device(struct ubi_device *ubi); struct ubi_device *ubi_get_by_major(int major); diff --git a/fs/fs.c b/fs/fs.c index c4b3583..440adae 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -1317,6 +1317,40 @@ } EXPORT_SYMBOL(mount); +static int fsdev_umount(struct fs_device_d *fsdev) +{ + return unregister_device(&fsdev->dev); +} + +/** + * umount_by_cdev Use a cdev struct to umount all mounted filesystems + * @param cdev cdev to the according device + * @return 0 on success or if cdev was not mounted, -errno otherwise + */ +int umount_by_cdev(struct cdev *cdev) +{ + struct fs_device_d *fs; + struct fs_device_d *fs_tmp; + int first_error = 0; + + for_each_fs_device_safe(fs_tmp, fs) { + int ret; + + if (fs->cdev == cdev) { + ret = fsdev_umount(fs); + if (ret) { + pr_err("Failed umounting %s, %d, continuing anyway\n", + fs->path, ret); + if (!first_error) + first_error = ret; + } + } + } + + return first_error; +} +EXPORT_SYMBOL(umount_by_cdev); + int umount(const char *pathname) { struct fs_device_d *fsdev = NULL, *f; @@ -1329,6 +1363,16 @@ } } + if (!fsdev) { + struct cdev *cdev = cdev_open(p, O_RDWR); + + if (cdev) { + free(p); + cdev_close(cdev); + return umount_by_cdev(cdev); + } + } + free(p); if (f == fs_dev_root && !list_is_singular(&fs_device_list)) { @@ -1341,9 +1385,7 @@ return -EFAULT; } - unregister_device(&fsdev->dev); - - return 0; + return fsdev_umount(fsdev); } EXPORT_SYMBOL(umount); diff --git a/include/fs.h b/include/fs.h index 9f4164e..b9d1e6e 100644 --- a/include/fs.h +++ b/include/fs.h @@ -88,6 +88,7 @@ extern struct list_head fs_device_list; #define for_each_fs_device(f) list_for_each_entry(f, &fs_device_list, list) +#define for_each_fs_device_safe(tmp, f) list_for_each_entry_safe(f, tmp, &fs_device_list, list) extern struct bus_type fs_bus; struct fs_device_d { @@ -143,6 +144,7 @@ int mount (const char *device, const char *fsname, const char *path, const char *fsoptions); int umount(const char *pathname); +int umount_by_cdev(struct cdev *cdev); /* not-so-standard functions */ int erase(int fd, loff_t count, loff_t offset); diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h index 2000ef2..8c02f96 100644 --- a/include/mtd/ubi-user.h +++ b/include/mtd/ubi-user.h @@ -406,6 +406,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset, int max_beb_per1024); -int ubi_detach_mtd_dev(int ubi_num, int anyway); +int ubi_detach(int ubi_num); +int ubi_num_get_by_mtd(struct mtd_info *mtd); #endif /* __UBI_USER_H__ */