162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. 462306a36Sopenharmony_ci * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is released under the GPL. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "dm-core.h" 1062306a36Sopenharmony_ci#include "dm-ima.h" 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/vmalloc.h> 1362306a36Sopenharmony_ci#include <linux/miscdevice.h> 1462306a36Sopenharmony_ci#include <linux/sched/mm.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/wait.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/rbtree.h> 1962306a36Sopenharmony_ci#include <linux/dm-ioctl.h> 2062306a36Sopenharmony_ci#include <linux/hdreg.h> 2162306a36Sopenharmony_ci#include <linux/compat.h> 2262306a36Sopenharmony_ci#include <linux/nospec.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/ima.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DM_MSG_PREFIX "ioctl" 2862306a36Sopenharmony_ci#define DM_DRIVER_EMAIL "dm-devel@redhat.com" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct dm_file { 3162306a36Sopenharmony_ci /* 3262306a36Sopenharmony_ci * poll will wait until the global event number is greater than 3362306a36Sopenharmony_ci * this value. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci volatile unsigned int global_event_nr; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci *--------------------------------------------------------------- 4062306a36Sopenharmony_ci * The ioctl interface needs to be able to look up devices by 4162306a36Sopenharmony_ci * name or uuid. 4262306a36Sopenharmony_ci *--------------------------------------------------------------- 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistruct hash_cell { 4562306a36Sopenharmony_ci struct rb_node name_node; 4662306a36Sopenharmony_ci struct rb_node uuid_node; 4762306a36Sopenharmony_ci bool name_set; 4862306a36Sopenharmony_ci bool uuid_set; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci char *name; 5162306a36Sopenharmony_ci char *uuid; 5262306a36Sopenharmony_ci struct mapped_device *md; 5362306a36Sopenharmony_ci struct dm_table *new_map; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct vers_iter { 5762306a36Sopenharmony_ci size_t param_size; 5862306a36Sopenharmony_ci struct dm_target_versions *vers, *old_vers; 5962306a36Sopenharmony_ci char *end; 6062306a36Sopenharmony_ci uint32_t flags; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct rb_root name_rb_tree = RB_ROOT; 6562306a36Sopenharmony_cistatic struct rb_root uuid_rb_tree = RB_ROOT; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Guards access to both hash tables. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic DECLARE_RWSEM(_hash_lock); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Protects use of mdptr to obtain hash cell name and uuid from mapped device. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic DEFINE_MUTEX(dm_hash_cells_mutex); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void dm_hash_exit(void) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci dm_hash_remove_all(false, false, false); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci *--------------------------------------------------------------- 8662306a36Sopenharmony_ci * Code for looking up a device by name 8762306a36Sopenharmony_ci *--------------------------------------------------------------- 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic struct hash_cell *__get_name_cell(const char *str) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct rb_node *n = name_rb_tree.rb_node; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci while (n) { 9462306a36Sopenharmony_ci struct hash_cell *hc = container_of(n, struct hash_cell, name_node); 9562306a36Sopenharmony_ci int c; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci c = strcmp(hc->name, str); 9862306a36Sopenharmony_ci if (!c) { 9962306a36Sopenharmony_ci dm_get(hc->md); 10062306a36Sopenharmony_ci return hc; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci n = c >= 0 ? n->rb_left : n->rb_right; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct hash_cell *__get_uuid_cell(const char *str) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct rb_node *n = uuid_rb_tree.rb_node; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci while (n) { 11362306a36Sopenharmony_ci struct hash_cell *hc = container_of(n, struct hash_cell, uuid_node); 11462306a36Sopenharmony_ci int c; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci c = strcmp(hc->uuid, str); 11762306a36Sopenharmony_ci if (!c) { 11862306a36Sopenharmony_ci dm_get(hc->md); 11962306a36Sopenharmony_ci return hc; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci n = c >= 0 ? n->rb_left : n->rb_right; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return NULL; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void __unlink_name(struct hash_cell *hc) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci if (hc->name_set) { 13062306a36Sopenharmony_ci hc->name_set = false; 13162306a36Sopenharmony_ci rb_erase(&hc->name_node, &name_rb_tree); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void __unlink_uuid(struct hash_cell *hc) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci if (hc->uuid_set) { 13862306a36Sopenharmony_ci hc->uuid_set = false; 13962306a36Sopenharmony_ci rb_erase(&hc->uuid_node, &uuid_rb_tree); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void __link_name(struct hash_cell *new_hc) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct rb_node **n, *parent; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci __unlink_name(new_hc); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci new_hc->name_set = true; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci n = &name_rb_tree.rb_node; 15262306a36Sopenharmony_ci parent = NULL; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci while (*n) { 15562306a36Sopenharmony_ci struct hash_cell *hc = container_of(*n, struct hash_cell, name_node); 15662306a36Sopenharmony_ci int c; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci c = strcmp(hc->name, new_hc->name); 15962306a36Sopenharmony_ci BUG_ON(!c); 16062306a36Sopenharmony_ci parent = *n; 16162306a36Sopenharmony_ci n = c >= 0 ? &hc->name_node.rb_left : &hc->name_node.rb_right; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci rb_link_node(&new_hc->name_node, parent, n); 16562306a36Sopenharmony_ci rb_insert_color(&new_hc->name_node, &name_rb_tree); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void __link_uuid(struct hash_cell *new_hc) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct rb_node **n, *parent; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci __unlink_uuid(new_hc); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci new_hc->uuid_set = true; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci n = &uuid_rb_tree.rb_node; 17762306a36Sopenharmony_ci parent = NULL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci while (*n) { 18062306a36Sopenharmony_ci struct hash_cell *hc = container_of(*n, struct hash_cell, uuid_node); 18162306a36Sopenharmony_ci int c; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci c = strcmp(hc->uuid, new_hc->uuid); 18462306a36Sopenharmony_ci BUG_ON(!c); 18562306a36Sopenharmony_ci parent = *n; 18662306a36Sopenharmony_ci n = c > 0 ? &hc->uuid_node.rb_left : &hc->uuid_node.rb_right; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci rb_link_node(&new_hc->uuid_node, parent, n); 19062306a36Sopenharmony_ci rb_insert_color(&new_hc->uuid_node, &uuid_rb_tree); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct hash_cell *__get_dev_cell(uint64_t dev) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct mapped_device *md; 19662306a36Sopenharmony_ci struct hash_cell *hc; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci md = dm_get_md(huge_decode_dev(dev)); 19962306a36Sopenharmony_ci if (!md) 20062306a36Sopenharmony_ci return NULL; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci hc = dm_get_mdptr(md); 20362306a36Sopenharmony_ci if (!hc) { 20462306a36Sopenharmony_ci dm_put(md); 20562306a36Sopenharmony_ci return NULL; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return hc; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* 21262306a36Sopenharmony_ci *--------------------------------------------------------------- 21362306a36Sopenharmony_ci * Inserting, removing and renaming a device. 21462306a36Sopenharmony_ci *--------------------------------------------------------------- 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic struct hash_cell *alloc_cell(const char *name, const char *uuid, 21762306a36Sopenharmony_ci struct mapped_device *md) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct hash_cell *hc; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci hc = kmalloc(sizeof(*hc), GFP_KERNEL); 22262306a36Sopenharmony_ci if (!hc) 22362306a36Sopenharmony_ci return NULL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci hc->name = kstrdup(name, GFP_KERNEL); 22662306a36Sopenharmony_ci if (!hc->name) { 22762306a36Sopenharmony_ci kfree(hc); 22862306a36Sopenharmony_ci return NULL; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (!uuid) 23262306a36Sopenharmony_ci hc->uuid = NULL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci else { 23562306a36Sopenharmony_ci hc->uuid = kstrdup(uuid, GFP_KERNEL); 23662306a36Sopenharmony_ci if (!hc->uuid) { 23762306a36Sopenharmony_ci kfree(hc->name); 23862306a36Sopenharmony_ci kfree(hc); 23962306a36Sopenharmony_ci return NULL; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci hc->name_set = hc->uuid_set = false; 24462306a36Sopenharmony_ci hc->md = md; 24562306a36Sopenharmony_ci hc->new_map = NULL; 24662306a36Sopenharmony_ci return hc; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void free_cell(struct hash_cell *hc) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci if (hc) { 25262306a36Sopenharmony_ci kfree(hc->name); 25362306a36Sopenharmony_ci kfree(hc->uuid); 25462306a36Sopenharmony_ci kfree(hc); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* 25962306a36Sopenharmony_ci * The kdev_t and uuid of a device can never change once it is 26062306a36Sopenharmony_ci * initially inserted. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_cistatic int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct hash_cell *cell, *hc; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * Allocate the new cells. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci cell = alloc_cell(name, uuid, md); 27062306a36Sopenharmony_ci if (!cell) 27162306a36Sopenharmony_ci return -ENOMEM; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * Insert the cell into both hash tables. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci down_write(&_hash_lock); 27762306a36Sopenharmony_ci hc = __get_name_cell(name); 27862306a36Sopenharmony_ci if (hc) { 27962306a36Sopenharmony_ci dm_put(hc->md); 28062306a36Sopenharmony_ci goto bad; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci __link_name(cell); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (uuid) { 28662306a36Sopenharmony_ci hc = __get_uuid_cell(uuid); 28762306a36Sopenharmony_ci if (hc) { 28862306a36Sopenharmony_ci __unlink_name(cell); 28962306a36Sopenharmony_ci dm_put(hc->md); 29062306a36Sopenharmony_ci goto bad; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci __link_uuid(cell); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci dm_get(md); 29562306a36Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 29662306a36Sopenharmony_ci dm_set_mdptr(md, cell); 29762306a36Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 29862306a36Sopenharmony_ci up_write(&_hash_lock); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci bad: 30362306a36Sopenharmony_ci up_write(&_hash_lock); 30462306a36Sopenharmony_ci free_cell(cell); 30562306a36Sopenharmony_ci return -EBUSY; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic struct dm_table *__hash_remove(struct hash_cell *hc) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct dm_table *table; 31162306a36Sopenharmony_ci int srcu_idx; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci lockdep_assert_held(&_hash_lock); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* remove from the dev trees */ 31662306a36Sopenharmony_ci __unlink_name(hc); 31762306a36Sopenharmony_ci __unlink_uuid(hc); 31862306a36Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 31962306a36Sopenharmony_ci dm_set_mdptr(hc->md, NULL); 32062306a36Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci table = dm_get_live_table(hc->md, &srcu_idx); 32362306a36Sopenharmony_ci if (table) 32462306a36Sopenharmony_ci dm_table_event(table); 32562306a36Sopenharmony_ci dm_put_live_table(hc->md, srcu_idx); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci table = NULL; 32862306a36Sopenharmony_ci if (hc->new_map) 32962306a36Sopenharmony_ci table = hc->new_map; 33062306a36Sopenharmony_ci dm_put(hc->md); 33162306a36Sopenharmony_ci free_cell(hc); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return table; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci int dev_skipped; 33962306a36Sopenharmony_ci struct rb_node *n; 34062306a36Sopenharmony_ci struct hash_cell *hc; 34162306a36Sopenharmony_ci struct mapped_device *md; 34262306a36Sopenharmony_ci struct dm_table *t; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciretry: 34562306a36Sopenharmony_ci dev_skipped = 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci down_write(&_hash_lock); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) { 35062306a36Sopenharmony_ci hc = container_of(n, struct hash_cell, name_node); 35162306a36Sopenharmony_ci md = hc->md; 35262306a36Sopenharmony_ci dm_get(md); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (keep_open_devices && 35562306a36Sopenharmony_ci dm_lock_for_deletion(md, mark_deferred, only_deferred)) { 35662306a36Sopenharmony_ci dm_put(md); 35762306a36Sopenharmony_ci dev_skipped++; 35862306a36Sopenharmony_ci continue; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci t = __hash_remove(hc); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci up_write(&_hash_lock); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (t) { 36662306a36Sopenharmony_ci dm_sync_table(md); 36762306a36Sopenharmony_ci dm_table_destroy(t); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci dm_ima_measure_on_device_remove(md, true); 37062306a36Sopenharmony_ci dm_put(md); 37162306a36Sopenharmony_ci if (likely(keep_open_devices)) 37262306a36Sopenharmony_ci dm_destroy(md); 37362306a36Sopenharmony_ci else 37462306a36Sopenharmony_ci dm_destroy_immediate(md); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* 37762306a36Sopenharmony_ci * Some mapped devices may be using other mapped 37862306a36Sopenharmony_ci * devices, so repeat until we make no further 37962306a36Sopenharmony_ci * progress. If a new mapped device is created 38062306a36Sopenharmony_ci * here it will also get removed. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci goto retry; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci up_write(&_hash_lock); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (dev_skipped) 38862306a36Sopenharmony_ci DMWARN("remove_all left %d open device(s)", dev_skipped); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* 39262306a36Sopenharmony_ci * Set the uuid of a hash_cell that isn't already set. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic void __set_cell_uuid(struct hash_cell *hc, char *new_uuid) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 39762306a36Sopenharmony_ci hc->uuid = new_uuid; 39862306a36Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci __link_uuid(hc); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * Changes the name of a hash_cell and returns the old name for 40562306a36Sopenharmony_ci * the caller to free. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_cistatic char *__change_cell_name(struct hash_cell *hc, char *new_name) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci char *old_name; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * Rename and move the name cell. 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci __unlink_name(hc); 41562306a36Sopenharmony_ci old_name = hc->name; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 41862306a36Sopenharmony_ci hc->name = new_name; 41962306a36Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci __link_name(hc); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return old_name; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic struct mapped_device *dm_hash_rename(struct dm_ioctl *param, 42762306a36Sopenharmony_ci const char *new) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci char *new_data, *old_name = NULL; 43062306a36Sopenharmony_ci struct hash_cell *hc; 43162306a36Sopenharmony_ci struct dm_table *table; 43262306a36Sopenharmony_ci struct mapped_device *md; 43362306a36Sopenharmony_ci unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; 43462306a36Sopenharmony_ci int srcu_idx; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * duplicate new. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_ci new_data = kstrdup(new, GFP_KERNEL); 44062306a36Sopenharmony_ci if (!new_data) 44162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci down_write(&_hash_lock); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Is new free ? 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci if (change_uuid) 44962306a36Sopenharmony_ci hc = __get_uuid_cell(new); 45062306a36Sopenharmony_ci else 45162306a36Sopenharmony_ci hc = __get_name_cell(new); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (hc) { 45462306a36Sopenharmony_ci DMERR("Unable to change %s on mapped device %s to one that already exists: %s", 45562306a36Sopenharmony_ci change_uuid ? "uuid" : "name", 45662306a36Sopenharmony_ci param->name, new); 45762306a36Sopenharmony_ci dm_put(hc->md); 45862306a36Sopenharmony_ci up_write(&_hash_lock); 45962306a36Sopenharmony_ci kfree(new_data); 46062306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* 46462306a36Sopenharmony_ci * Is there such a device as 'old' ? 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci hc = __get_name_cell(param->name); 46762306a36Sopenharmony_ci if (!hc) { 46862306a36Sopenharmony_ci DMERR("Unable to rename non-existent device, %s to %s%s", 46962306a36Sopenharmony_ci param->name, change_uuid ? "uuid " : "", new); 47062306a36Sopenharmony_ci up_write(&_hash_lock); 47162306a36Sopenharmony_ci kfree(new_data); 47262306a36Sopenharmony_ci return ERR_PTR(-ENXIO); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Does this device already have a uuid? 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci if (change_uuid && hc->uuid) { 47962306a36Sopenharmony_ci DMERR("Unable to change uuid of mapped device %s to %s " 48062306a36Sopenharmony_ci "because uuid is already set to %s", 48162306a36Sopenharmony_ci param->name, new, hc->uuid); 48262306a36Sopenharmony_ci dm_put(hc->md); 48362306a36Sopenharmony_ci up_write(&_hash_lock); 48462306a36Sopenharmony_ci kfree(new_data); 48562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (change_uuid) 48962306a36Sopenharmony_ci __set_cell_uuid(hc, new_data); 49062306a36Sopenharmony_ci else 49162306a36Sopenharmony_ci old_name = __change_cell_name(hc, new_data); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* 49462306a36Sopenharmony_ci * Wake up any dm event waiters. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci table = dm_get_live_table(hc->md, &srcu_idx); 49762306a36Sopenharmony_ci if (table) 49862306a36Sopenharmony_ci dm_table_event(table); 49962306a36Sopenharmony_ci dm_put_live_table(hc->md, srcu_idx); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr, false)) 50262306a36Sopenharmony_ci param->flags |= DM_UEVENT_GENERATED_FLAG; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci md = hc->md; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci dm_ima_measure_on_device_rename(md); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci up_write(&_hash_lock); 50962306a36Sopenharmony_ci kfree(old_name); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return md; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_civoid dm_deferred_remove(void) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci dm_hash_remove_all(true, false, true); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* 52062306a36Sopenharmony_ci *--------------------------------------------------------------- 52162306a36Sopenharmony_ci * Implementation of the ioctl commands 52262306a36Sopenharmony_ci *--------------------------------------------------------------- 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci/* 52562306a36Sopenharmony_ci * All the ioctl commands get dispatched to functions with this 52662306a36Sopenharmony_ci * prototype. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_citypedef int (*ioctl_fn)(struct file *filp, struct dm_ioctl *param, size_t param_size); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic int remove_all(struct file *filp, struct dm_ioctl *param, size_t param_size) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci dm_hash_remove_all(true, !!(param->flags & DM_DEFERRED_REMOVE), false); 53362306a36Sopenharmony_ci param->data_size = 0; 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/* 53862306a36Sopenharmony_ci * Round up the ptr to an 8-byte boundary. 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci#define ALIGN_MASK 7 54162306a36Sopenharmony_cistatic inline size_t align_val(size_t val) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci return (val + ALIGN_MASK) & ~ALIGN_MASK; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_cistatic inline void *align_ptr(void *ptr) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci return (void *)align_val((size_t)ptr); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/* 55162306a36Sopenharmony_ci * Retrieves the data payload buffer from an already allocated 55262306a36Sopenharmony_ci * struct dm_ioctl. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_cistatic void *get_result_buffer(struct dm_ioctl *param, size_t param_size, 55562306a36Sopenharmony_ci size_t *len) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci param->data_start = align_ptr(param + 1) - (void *) param; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (param->data_start < param_size) 56062306a36Sopenharmony_ci *len = param_size - param->data_start; 56162306a36Sopenharmony_ci else 56262306a36Sopenharmony_ci *len = 0; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return ((void *) param) + param->data_start; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic bool filter_device(struct hash_cell *hc, const char *pfx_name, const char *pfx_uuid) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci const char *val; 57062306a36Sopenharmony_ci size_t val_len, pfx_len; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci val = hc->name; 57362306a36Sopenharmony_ci val_len = strlen(val); 57462306a36Sopenharmony_ci pfx_len = strnlen(pfx_name, DM_NAME_LEN); 57562306a36Sopenharmony_ci if (pfx_len > val_len) 57662306a36Sopenharmony_ci return false; 57762306a36Sopenharmony_ci if (memcmp(val, pfx_name, pfx_len)) 57862306a36Sopenharmony_ci return false; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci val = hc->uuid ? hc->uuid : ""; 58162306a36Sopenharmony_ci val_len = strlen(val); 58262306a36Sopenharmony_ci pfx_len = strnlen(pfx_uuid, DM_UUID_LEN); 58362306a36Sopenharmony_ci if (pfx_len > val_len) 58462306a36Sopenharmony_ci return false; 58562306a36Sopenharmony_ci if (memcmp(val, pfx_uuid, pfx_len)) 58662306a36Sopenharmony_ci return false; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return true; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_size) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct rb_node *n; 59462306a36Sopenharmony_ci struct hash_cell *hc; 59562306a36Sopenharmony_ci size_t len, needed = 0; 59662306a36Sopenharmony_ci struct gendisk *disk; 59762306a36Sopenharmony_ci struct dm_name_list *orig_nl, *nl, *old_nl = NULL; 59862306a36Sopenharmony_ci uint32_t *event_nr; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci down_write(&_hash_lock); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * Loop through all the devices working out how much 60462306a36Sopenharmony_ci * space we need. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) { 60762306a36Sopenharmony_ci hc = container_of(n, struct hash_cell, name_node); 60862306a36Sopenharmony_ci if (!filter_device(hc, param->name, param->uuid)) 60962306a36Sopenharmony_ci continue; 61062306a36Sopenharmony_ci needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1); 61162306a36Sopenharmony_ci needed += align_val(sizeof(uint32_t) * 2); 61262306a36Sopenharmony_ci if (param->flags & DM_UUID_FLAG && hc->uuid) 61362306a36Sopenharmony_ci needed += align_val(strlen(hc->uuid) + 1); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* 61762306a36Sopenharmony_ci * Grab our output buffer. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci nl = orig_nl = get_result_buffer(param, param_size, &len); 62062306a36Sopenharmony_ci if (len < needed || len < sizeof(nl->dev)) { 62162306a36Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 62262306a36Sopenharmony_ci goto out; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci param->data_size = param->data_start + needed; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci nl->dev = 0; /* Flags no data */ 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* 62962306a36Sopenharmony_ci * Now loop through filling out the names. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) { 63262306a36Sopenharmony_ci void *uuid_ptr; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci hc = container_of(n, struct hash_cell, name_node); 63562306a36Sopenharmony_ci if (!filter_device(hc, param->name, param->uuid)) 63662306a36Sopenharmony_ci continue; 63762306a36Sopenharmony_ci if (old_nl) 63862306a36Sopenharmony_ci old_nl->next = (uint32_t) ((void *) nl - 63962306a36Sopenharmony_ci (void *) old_nl); 64062306a36Sopenharmony_ci disk = dm_disk(hc->md); 64162306a36Sopenharmony_ci nl->dev = huge_encode_dev(disk_devt(disk)); 64262306a36Sopenharmony_ci nl->next = 0; 64362306a36Sopenharmony_ci strcpy(nl->name, hc->name); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci old_nl = nl; 64662306a36Sopenharmony_ci event_nr = align_ptr(nl->name + strlen(hc->name) + 1); 64762306a36Sopenharmony_ci event_nr[0] = dm_get_event_nr(hc->md); 64862306a36Sopenharmony_ci event_nr[1] = 0; 64962306a36Sopenharmony_ci uuid_ptr = align_ptr(event_nr + 2); 65062306a36Sopenharmony_ci if (param->flags & DM_UUID_FLAG) { 65162306a36Sopenharmony_ci if (hc->uuid) { 65262306a36Sopenharmony_ci event_nr[1] |= DM_NAME_LIST_FLAG_HAS_UUID; 65362306a36Sopenharmony_ci strcpy(uuid_ptr, hc->uuid); 65462306a36Sopenharmony_ci uuid_ptr = align_ptr(uuid_ptr + strlen(hc->uuid) + 1); 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci event_nr[1] |= DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci nl = uuid_ptr; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci /* 66262306a36Sopenharmony_ci * If mismatch happens, security may be compromised due to buffer 66362306a36Sopenharmony_ci * overflow, so it's better to crash. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci BUG_ON((char *)nl - (char *)orig_nl != needed); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci out: 66862306a36Sopenharmony_ci up_write(&_hash_lock); 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void list_version_get_needed(struct target_type *tt, void *needed_param) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci size_t *needed = needed_param; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci *needed += sizeof(struct dm_target_versions); 67762306a36Sopenharmony_ci *needed += strlen(tt->name) + 1; 67862306a36Sopenharmony_ci *needed += ALIGN_MASK; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void list_version_get_info(struct target_type *tt, void *param) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct vers_iter *info = param; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* Check space - it might have changed since the first iteration */ 68662306a36Sopenharmony_ci if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) { 68762306a36Sopenharmony_ci info->flags = DM_BUFFER_FULL_FLAG; 68862306a36Sopenharmony_ci return; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (info->old_vers) 69262306a36Sopenharmony_ci info->old_vers->next = (uint32_t) ((void *)info->vers - (void *)info->old_vers); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci info->vers->version[0] = tt->version[0]; 69562306a36Sopenharmony_ci info->vers->version[1] = tt->version[1]; 69662306a36Sopenharmony_ci info->vers->version[2] = tt->version[2]; 69762306a36Sopenharmony_ci info->vers->next = 0; 69862306a36Sopenharmony_ci strcpy(info->vers->name, tt->name); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci info->old_vers = info->vers; 70162306a36Sopenharmony_ci info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci size_t len, needed = 0; 70762306a36Sopenharmony_ci struct dm_target_versions *vers; 70862306a36Sopenharmony_ci struct vers_iter iter_info; 70962306a36Sopenharmony_ci struct target_type *tt = NULL; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (name) { 71262306a36Sopenharmony_ci tt = dm_get_target_type(name); 71362306a36Sopenharmony_ci if (!tt) 71462306a36Sopenharmony_ci return -EINVAL; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* 71862306a36Sopenharmony_ci * Loop through all the devices working out how much 71962306a36Sopenharmony_ci * space we need. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci if (!tt) 72262306a36Sopenharmony_ci dm_target_iterate(list_version_get_needed, &needed); 72362306a36Sopenharmony_ci else 72462306a36Sopenharmony_ci list_version_get_needed(tt, &needed); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* 72762306a36Sopenharmony_ci * Grab our output buffer. 72862306a36Sopenharmony_ci */ 72962306a36Sopenharmony_ci vers = get_result_buffer(param, param_size, &len); 73062306a36Sopenharmony_ci if (len < needed) { 73162306a36Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 73262306a36Sopenharmony_ci goto out; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci param->data_size = param->data_start + needed; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci iter_info.param_size = param_size; 73762306a36Sopenharmony_ci iter_info.old_vers = NULL; 73862306a36Sopenharmony_ci iter_info.vers = vers; 73962306a36Sopenharmony_ci iter_info.flags = 0; 74062306a36Sopenharmony_ci iter_info.end = (char *)vers + needed; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* 74362306a36Sopenharmony_ci * Now loop through filling out the names & versions. 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_ci if (!tt) 74662306a36Sopenharmony_ci dm_target_iterate(list_version_get_info, &iter_info); 74762306a36Sopenharmony_ci else 74862306a36Sopenharmony_ci list_version_get_info(tt, &iter_info); 74962306a36Sopenharmony_ci param->flags |= iter_info.flags; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci out: 75262306a36Sopenharmony_ci if (tt) 75362306a36Sopenharmony_ci dm_put_target_type(tt); 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int list_versions(struct file *filp, struct dm_ioctl *param, size_t param_size) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci return __list_versions(param, param_size, NULL); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int get_target_version(struct file *filp, struct dm_ioctl *param, size_t param_size) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci return __list_versions(param, param_size, param->name); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic int check_name(const char *name) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci if (strchr(name, '/')) { 77062306a36Sopenharmony_ci DMERR("device name cannot contain '/'"); 77162306a36Sopenharmony_ci return -EINVAL; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (strcmp(name, DM_CONTROL_NODE) == 0 || 77562306a36Sopenharmony_ci strcmp(name, ".") == 0 || 77662306a36Sopenharmony_ci strcmp(name, "..") == 0) { 77762306a36Sopenharmony_ci DMERR("device name cannot be \"%s\", \".\", or \"..\"", DM_CONTROL_NODE); 77862306a36Sopenharmony_ci return -EINVAL; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return 0; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci/* 78562306a36Sopenharmony_ci * On successful return, the caller must not attempt to acquire 78662306a36Sopenharmony_ci * _hash_lock without first calling dm_put_live_table, because dm_table_destroy 78762306a36Sopenharmony_ci * waits for this dm_put_live_table and could be called under this lock. 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_cistatic struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct hash_cell *hc; 79262306a36Sopenharmony_ci struct dm_table *table = NULL; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* increment rcu count, we don't care about the table pointer */ 79562306a36Sopenharmony_ci dm_get_live_table(md, srcu_idx); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci down_read(&_hash_lock); 79862306a36Sopenharmony_ci hc = dm_get_mdptr(md); 79962306a36Sopenharmony_ci if (!hc) { 80062306a36Sopenharmony_ci DMERR("device has been removed from the dev hash table."); 80162306a36Sopenharmony_ci goto out; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci table = hc->new_map; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ciout: 80762306a36Sopenharmony_ci up_read(&_hash_lock); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci return table; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md, 81362306a36Sopenharmony_ci struct dm_ioctl *param, 81462306a36Sopenharmony_ci int *srcu_idx) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ? 81762306a36Sopenharmony_ci dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/* 82162306a36Sopenharmony_ci * Fills in a dm_ioctl structure, ready for sending back to 82262306a36Sopenharmony_ci * userland. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_cistatic void __dev_status(struct mapped_device *md, struct dm_ioctl *param) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct gendisk *disk = dm_disk(md); 82762306a36Sopenharmony_ci struct dm_table *table; 82862306a36Sopenharmony_ci int srcu_idx; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | 83162306a36Sopenharmony_ci DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (dm_suspended_md(md)) 83462306a36Sopenharmony_ci param->flags |= DM_SUSPEND_FLAG; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (dm_suspended_internally_md(md)) 83762306a36Sopenharmony_ci param->flags |= DM_INTERNAL_SUSPEND_FLAG; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (dm_test_deferred_remove_flag(md)) 84062306a36Sopenharmony_ci param->flags |= DM_DEFERRED_REMOVE; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci param->dev = huge_encode_dev(disk_devt(disk)); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* 84562306a36Sopenharmony_ci * Yes, this will be out of date by the time it gets back 84662306a36Sopenharmony_ci * to userland, but it is still very useful for 84762306a36Sopenharmony_ci * debugging. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_ci param->open_count = dm_open_count(md); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci param->event_nr = dm_get_event_nr(md); 85262306a36Sopenharmony_ci param->target_count = 0; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci table = dm_get_live_table(md, &srcu_idx); 85562306a36Sopenharmony_ci if (table) { 85662306a36Sopenharmony_ci if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) { 85762306a36Sopenharmony_ci if (get_disk_ro(disk)) 85862306a36Sopenharmony_ci param->flags |= DM_READONLY_FLAG; 85962306a36Sopenharmony_ci param->target_count = table->num_targets; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci param->flags |= DM_ACTIVE_PRESENT_FLAG; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci dm_put_live_table(md, srcu_idx); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) { 86762306a36Sopenharmony_ci int srcu_idx; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci table = dm_get_inactive_table(md, &srcu_idx); 87062306a36Sopenharmony_ci if (table) { 87162306a36Sopenharmony_ci if (!(dm_table_get_mode(table) & BLK_OPEN_WRITE)) 87262306a36Sopenharmony_ci param->flags |= DM_READONLY_FLAG; 87362306a36Sopenharmony_ci param->target_count = table->num_targets; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci dm_put_live_table(md, srcu_idx); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic int dev_create(struct file *filp, struct dm_ioctl *param, size_t param_size) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci int r, m = DM_ANY_MINOR; 88262306a36Sopenharmony_ci struct mapped_device *md; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci r = check_name(param->name); 88562306a36Sopenharmony_ci if (r) 88662306a36Sopenharmony_ci return r; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (param->flags & DM_PERSISTENT_DEV_FLAG) 88962306a36Sopenharmony_ci m = MINOR(huge_decode_dev(param->dev)); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci r = dm_create(m, &md); 89262306a36Sopenharmony_ci if (r) 89362306a36Sopenharmony_ci return r; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md); 89662306a36Sopenharmony_ci if (r) { 89762306a36Sopenharmony_ci dm_put(md); 89862306a36Sopenharmony_ci dm_destroy(md); 89962306a36Sopenharmony_ci return r; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci __dev_status(md, param); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci dm_put(md); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci return 0; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci/* 91262306a36Sopenharmony_ci * Always use UUID for lookups if it's present, otherwise use name or dev. 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_cistatic struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct hash_cell *hc = NULL; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (*param->uuid) { 91962306a36Sopenharmony_ci if (*param->name || param->dev) { 92062306a36Sopenharmony_ci DMERR("Invalid ioctl structure: uuid %s, name %s, dev %llx", 92162306a36Sopenharmony_ci param->uuid, param->name, (unsigned long long)param->dev); 92262306a36Sopenharmony_ci return NULL; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci hc = __get_uuid_cell(param->uuid); 92662306a36Sopenharmony_ci if (!hc) 92762306a36Sopenharmony_ci return NULL; 92862306a36Sopenharmony_ci } else if (*param->name) { 92962306a36Sopenharmony_ci if (param->dev) { 93062306a36Sopenharmony_ci DMERR("Invalid ioctl structure: name %s, dev %llx", 93162306a36Sopenharmony_ci param->name, (unsigned long long)param->dev); 93262306a36Sopenharmony_ci return NULL; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci hc = __get_name_cell(param->name); 93662306a36Sopenharmony_ci if (!hc) 93762306a36Sopenharmony_ci return NULL; 93862306a36Sopenharmony_ci } else if (param->dev) { 93962306a36Sopenharmony_ci hc = __get_dev_cell(param->dev); 94062306a36Sopenharmony_ci if (!hc) 94162306a36Sopenharmony_ci return NULL; 94262306a36Sopenharmony_ci } else 94362306a36Sopenharmony_ci return NULL; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* 94662306a36Sopenharmony_ci * Sneakily write in both the name and the uuid 94762306a36Sopenharmony_ci * while we have the cell. 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_ci strscpy(param->name, hc->name, sizeof(param->name)); 95062306a36Sopenharmony_ci if (hc->uuid) 95162306a36Sopenharmony_ci strscpy(param->uuid, hc->uuid, sizeof(param->uuid)); 95262306a36Sopenharmony_ci else 95362306a36Sopenharmony_ci param->uuid[0] = '\0'; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (hc->new_map) 95662306a36Sopenharmony_ci param->flags |= DM_INACTIVE_PRESENT_FLAG; 95762306a36Sopenharmony_ci else 95862306a36Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return hc; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic struct mapped_device *find_device(struct dm_ioctl *param) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct hash_cell *hc; 96662306a36Sopenharmony_ci struct mapped_device *md = NULL; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci down_read(&_hash_lock); 96962306a36Sopenharmony_ci hc = __find_device_hash_cell(param); 97062306a36Sopenharmony_ci if (hc) 97162306a36Sopenharmony_ci md = hc->md; 97262306a36Sopenharmony_ci up_read(&_hash_lock); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci return md; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_size) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct hash_cell *hc; 98062306a36Sopenharmony_ci struct mapped_device *md; 98162306a36Sopenharmony_ci int r; 98262306a36Sopenharmony_ci struct dm_table *t; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci down_write(&_hash_lock); 98562306a36Sopenharmony_ci hc = __find_device_hash_cell(param); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (!hc) { 98862306a36Sopenharmony_ci DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); 98962306a36Sopenharmony_ci up_write(&_hash_lock); 99062306a36Sopenharmony_ci return -ENXIO; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci md = hc->md; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* 99662306a36Sopenharmony_ci * Ensure the device is not open and nothing further can open it. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ci r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false); 99962306a36Sopenharmony_ci if (r) { 100062306a36Sopenharmony_ci if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) { 100162306a36Sopenharmony_ci up_write(&_hash_lock); 100262306a36Sopenharmony_ci dm_put(md); 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci DMDEBUG_LIMIT("unable to remove open device %s", hc->name); 100662306a36Sopenharmony_ci up_write(&_hash_lock); 100762306a36Sopenharmony_ci dm_put(md); 100862306a36Sopenharmony_ci return r; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci t = __hash_remove(hc); 101262306a36Sopenharmony_ci up_write(&_hash_lock); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (t) { 101562306a36Sopenharmony_ci dm_sync_table(md); 101662306a36Sopenharmony_ci dm_table_destroy(t); 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci param->flags &= ~DM_DEFERRED_REMOVE; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci dm_ima_measure_on_device_remove(md, false); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false)) 102462306a36Sopenharmony_ci param->flags |= DM_UEVENT_GENERATED_FLAG; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci dm_put(md); 102762306a36Sopenharmony_ci dm_destroy(md); 102862306a36Sopenharmony_ci return 0; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci/* 103262306a36Sopenharmony_ci * Check a string doesn't overrun the chunk of 103362306a36Sopenharmony_ci * memory we copied from userland. 103462306a36Sopenharmony_ci */ 103562306a36Sopenharmony_cistatic int invalid_str(char *str, void *end) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci while ((void *) str < end) 103862306a36Sopenharmony_ci if (!*str++) 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return -EINVAL; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic int dev_rename(struct file *filp, struct dm_ioctl *param, size_t param_size) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci int r; 104762306a36Sopenharmony_ci char *new_data = (char *) param + param->data_start; 104862306a36Sopenharmony_ci struct mapped_device *md; 104962306a36Sopenharmony_ci unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (new_data < param->data || 105262306a36Sopenharmony_ci invalid_str(new_data, (void *) param + param_size) || !*new_data || 105362306a36Sopenharmony_ci strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) { 105462306a36Sopenharmony_ci DMERR("Invalid new mapped device name or uuid string supplied."); 105562306a36Sopenharmony_ci return -EINVAL; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (!change_uuid) { 105962306a36Sopenharmony_ci r = check_name(new_data); 106062306a36Sopenharmony_ci if (r) 106162306a36Sopenharmony_ci return r; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci md = dm_hash_rename(param, new_data); 106562306a36Sopenharmony_ci if (IS_ERR(md)) 106662306a36Sopenharmony_ci return PTR_ERR(md); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci __dev_status(md, param); 106962306a36Sopenharmony_ci dm_put(md); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic int dev_set_geometry(struct file *filp, struct dm_ioctl *param, size_t param_size) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci int r = -EINVAL, x; 107762306a36Sopenharmony_ci struct mapped_device *md; 107862306a36Sopenharmony_ci struct hd_geometry geometry; 107962306a36Sopenharmony_ci unsigned long indata[4]; 108062306a36Sopenharmony_ci char *geostr = (char *) param + param->data_start; 108162306a36Sopenharmony_ci char dummy; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci md = find_device(param); 108462306a36Sopenharmony_ci if (!md) 108562306a36Sopenharmony_ci return -ENXIO; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (geostr < param->data || 108862306a36Sopenharmony_ci invalid_str(geostr, (void *) param + param_size)) { 108962306a36Sopenharmony_ci DMERR("Invalid geometry supplied."); 109062306a36Sopenharmony_ci goto out; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci x = sscanf(geostr, "%lu %lu %lu %lu%c", indata, 109462306a36Sopenharmony_ci indata + 1, indata + 2, indata + 3, &dummy); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci if (x != 4) { 109762306a36Sopenharmony_ci DMERR("Unable to interpret geometry settings."); 109862306a36Sopenharmony_ci goto out; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (indata[0] > 65535 || indata[1] > 255 || indata[2] > 255) { 110262306a36Sopenharmony_ci DMERR("Geometry exceeds range limits."); 110362306a36Sopenharmony_ci goto out; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci geometry.cylinders = indata[0]; 110762306a36Sopenharmony_ci geometry.heads = indata[1]; 110862306a36Sopenharmony_ci geometry.sectors = indata[2]; 110962306a36Sopenharmony_ci geometry.start = indata[3]; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci r = dm_set_geometry(md, &geometry); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci param->data_size = 0; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ciout: 111662306a36Sopenharmony_ci dm_put(md); 111762306a36Sopenharmony_ci return r; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic int do_suspend(struct dm_ioctl *param) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci int r = 0; 112362306a36Sopenharmony_ci unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG; 112462306a36Sopenharmony_ci struct mapped_device *md; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci md = find_device(param); 112762306a36Sopenharmony_ci if (!md) 112862306a36Sopenharmony_ci return -ENXIO; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (param->flags & DM_SKIP_LOCKFS_FLAG) 113162306a36Sopenharmony_ci suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; 113262306a36Sopenharmony_ci if (param->flags & DM_NOFLUSH_FLAG) 113362306a36Sopenharmony_ci suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (!dm_suspended_md(md)) { 113662306a36Sopenharmony_ci r = dm_suspend(md, suspend_flags); 113762306a36Sopenharmony_ci if (r) 113862306a36Sopenharmony_ci goto out; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci __dev_status(md, param); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ciout: 114462306a36Sopenharmony_ci dm_put(md); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci return r; 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic int do_resume(struct dm_ioctl *param) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci int r = 0; 115262306a36Sopenharmony_ci unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG; 115362306a36Sopenharmony_ci struct hash_cell *hc; 115462306a36Sopenharmony_ci struct mapped_device *md; 115562306a36Sopenharmony_ci struct dm_table *new_map, *old_map = NULL; 115662306a36Sopenharmony_ci bool need_resize_uevent = false; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci down_write(&_hash_lock); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci hc = __find_device_hash_cell(param); 116162306a36Sopenharmony_ci if (!hc) { 116262306a36Sopenharmony_ci DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); 116362306a36Sopenharmony_ci up_write(&_hash_lock); 116462306a36Sopenharmony_ci return -ENXIO; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci md = hc->md; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci new_map = hc->new_map; 117062306a36Sopenharmony_ci hc->new_map = NULL; 117162306a36Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci up_write(&_hash_lock); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* Do we need to load a new map ? */ 117662306a36Sopenharmony_ci if (new_map) { 117762306a36Sopenharmony_ci sector_t old_size, new_size; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* Suspend if it isn't already suspended */ 118062306a36Sopenharmony_ci if (param->flags & DM_SKIP_LOCKFS_FLAG) 118162306a36Sopenharmony_ci suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; 118262306a36Sopenharmony_ci if (param->flags & DM_NOFLUSH_FLAG) 118362306a36Sopenharmony_ci suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; 118462306a36Sopenharmony_ci if (!dm_suspended_md(md)) 118562306a36Sopenharmony_ci dm_suspend(md, suspend_flags); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci old_size = dm_get_size(md); 118862306a36Sopenharmony_ci old_map = dm_swap_table(md, new_map); 118962306a36Sopenharmony_ci if (IS_ERR(old_map)) { 119062306a36Sopenharmony_ci dm_sync_table(md); 119162306a36Sopenharmony_ci dm_table_destroy(new_map); 119262306a36Sopenharmony_ci dm_put(md); 119362306a36Sopenharmony_ci return PTR_ERR(old_map); 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci new_size = dm_get_size(md); 119662306a36Sopenharmony_ci if (old_size && new_size && old_size != new_size) 119762306a36Sopenharmony_ci need_resize_uevent = true; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (dm_table_get_mode(new_map) & BLK_OPEN_WRITE) 120062306a36Sopenharmony_ci set_disk_ro(dm_disk(md), 0); 120162306a36Sopenharmony_ci else 120262306a36Sopenharmony_ci set_disk_ro(dm_disk(md), 1); 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (dm_suspended_md(md)) { 120662306a36Sopenharmony_ci r = dm_resume(md); 120762306a36Sopenharmony_ci if (!r) { 120862306a36Sopenharmony_ci dm_ima_measure_on_device_resume(md, new_map ? true : false); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent)) 121162306a36Sopenharmony_ci param->flags |= DM_UEVENT_GENERATED_FLAG; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci /* 121662306a36Sopenharmony_ci * Since dm_swap_table synchronizes RCU, nobody should be in 121762306a36Sopenharmony_ci * read-side critical section already. 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_ci if (old_map) 122062306a36Sopenharmony_ci dm_table_destroy(old_map); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (!r) 122362306a36Sopenharmony_ci __dev_status(md, param); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci dm_put(md); 122662306a36Sopenharmony_ci return r; 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci/* 123062306a36Sopenharmony_ci * Set or unset the suspension state of a device. 123162306a36Sopenharmony_ci * If the device already is in the requested state we just return its status. 123262306a36Sopenharmony_ci */ 123362306a36Sopenharmony_cistatic int dev_suspend(struct file *filp, struct dm_ioctl *param, size_t param_size) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci if (param->flags & DM_SUSPEND_FLAG) 123662306a36Sopenharmony_ci return do_suspend(param); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci return do_resume(param); 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci/* 124262306a36Sopenharmony_ci * Copies device info back to user space, used by 124362306a36Sopenharmony_ci * the create and info ioctls. 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_cistatic int dev_status(struct file *filp, struct dm_ioctl *param, size_t param_size) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct mapped_device *md; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci md = find_device(param); 125062306a36Sopenharmony_ci if (!md) 125162306a36Sopenharmony_ci return -ENXIO; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci __dev_status(md, param); 125462306a36Sopenharmony_ci dm_put(md); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci return 0; 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci/* 126062306a36Sopenharmony_ci * Build up the status struct for each target 126162306a36Sopenharmony_ci */ 126262306a36Sopenharmony_cistatic void retrieve_status(struct dm_table *table, 126362306a36Sopenharmony_ci struct dm_ioctl *param, size_t param_size) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci unsigned int i, num_targets; 126662306a36Sopenharmony_ci struct dm_target_spec *spec; 126762306a36Sopenharmony_ci char *outbuf, *outptr; 126862306a36Sopenharmony_ci status_type_t type; 126962306a36Sopenharmony_ci size_t remaining, len, used = 0; 127062306a36Sopenharmony_ci unsigned int status_flags = 0; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci outptr = outbuf = get_result_buffer(param, param_size, &len); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (param->flags & DM_STATUS_TABLE_FLAG) 127562306a36Sopenharmony_ci type = STATUSTYPE_TABLE; 127662306a36Sopenharmony_ci else if (param->flags & DM_IMA_MEASUREMENT_FLAG) 127762306a36Sopenharmony_ci type = STATUSTYPE_IMA; 127862306a36Sopenharmony_ci else 127962306a36Sopenharmony_ci type = STATUSTYPE_INFO; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci /* Get all the target info */ 128262306a36Sopenharmony_ci num_targets = table->num_targets; 128362306a36Sopenharmony_ci for (i = 0; i < num_targets; i++) { 128462306a36Sopenharmony_ci struct dm_target *ti = dm_table_get_target(table, i); 128562306a36Sopenharmony_ci size_t l; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci remaining = len - (outptr - outbuf); 128862306a36Sopenharmony_ci if (remaining <= sizeof(struct dm_target_spec)) { 128962306a36Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 129062306a36Sopenharmony_ci break; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci spec = (struct dm_target_spec *) outptr; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci spec->status = 0; 129662306a36Sopenharmony_ci spec->sector_start = ti->begin; 129762306a36Sopenharmony_ci spec->length = ti->len; 129862306a36Sopenharmony_ci strncpy(spec->target_type, ti->type->name, 129962306a36Sopenharmony_ci sizeof(spec->target_type) - 1); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci outptr += sizeof(struct dm_target_spec); 130262306a36Sopenharmony_ci remaining = len - (outptr - outbuf); 130362306a36Sopenharmony_ci if (remaining <= 0) { 130462306a36Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 130562306a36Sopenharmony_ci break; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci /* Get the status/table string from the target driver */ 130962306a36Sopenharmony_ci if (ti->type->status) { 131062306a36Sopenharmony_ci if (param->flags & DM_NOFLUSH_FLAG) 131162306a36Sopenharmony_ci status_flags |= DM_STATUS_NOFLUSH_FLAG; 131262306a36Sopenharmony_ci ti->type->status(ti, type, status_flags, outptr, remaining); 131362306a36Sopenharmony_ci } else 131462306a36Sopenharmony_ci outptr[0] = '\0'; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci l = strlen(outptr) + 1; 131762306a36Sopenharmony_ci if (l == remaining) { 131862306a36Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 131962306a36Sopenharmony_ci break; 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci outptr += l; 132362306a36Sopenharmony_ci used = param->data_start + (outptr - outbuf); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci outptr = align_ptr(outptr); 132662306a36Sopenharmony_ci spec->next = outptr - outbuf; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (used) 133062306a36Sopenharmony_ci param->data_size = used; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci param->target_count = num_targets; 133362306a36Sopenharmony_ci} 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci/* 133662306a36Sopenharmony_ci * Wait for a device to report an event 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_cistatic int dev_wait(struct file *filp, struct dm_ioctl *param, size_t param_size) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci int r = 0; 134162306a36Sopenharmony_ci struct mapped_device *md; 134262306a36Sopenharmony_ci struct dm_table *table; 134362306a36Sopenharmony_ci int srcu_idx; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci md = find_device(param); 134662306a36Sopenharmony_ci if (!md) 134762306a36Sopenharmony_ci return -ENXIO; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci /* 135062306a36Sopenharmony_ci * Wait for a notification event 135162306a36Sopenharmony_ci */ 135262306a36Sopenharmony_ci if (dm_wait_event(md, param->event_nr)) { 135362306a36Sopenharmony_ci r = -ERESTARTSYS; 135462306a36Sopenharmony_ci goto out; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci /* 135862306a36Sopenharmony_ci * The userland program is going to want to know what 135962306a36Sopenharmony_ci * changed to trigger the event, so we may as well tell 136062306a36Sopenharmony_ci * him and save an ioctl. 136162306a36Sopenharmony_ci */ 136262306a36Sopenharmony_ci __dev_status(md, param); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci table = dm_get_live_or_inactive_table(md, param, &srcu_idx); 136562306a36Sopenharmony_ci if (table) 136662306a36Sopenharmony_ci retrieve_status(table, param, param_size); 136762306a36Sopenharmony_ci dm_put_live_table(md, srcu_idx); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ciout: 137062306a36Sopenharmony_ci dm_put(md); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci return r; 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci/* 137662306a36Sopenharmony_ci * Remember the global event number and make it possible to poll 137762306a36Sopenharmony_ci * for further events. 137862306a36Sopenharmony_ci */ 137962306a36Sopenharmony_cistatic int dev_arm_poll(struct file *filp, struct dm_ioctl *param, size_t param_size) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct dm_file *priv = filp->private_data; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci priv->global_event_nr = atomic_read(&dm_global_event_nr); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci return 0; 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic inline blk_mode_t get_mode(struct dm_ioctl *param) 138962306a36Sopenharmony_ci{ 139062306a36Sopenharmony_ci blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_WRITE; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (param->flags & DM_READONLY_FLAG) 139362306a36Sopenharmony_ci mode = BLK_OPEN_READ; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci return mode; 139662306a36Sopenharmony_ci} 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_cistatic int next_target(struct dm_target_spec *last, uint32_t next, const char *end, 139962306a36Sopenharmony_ci struct dm_target_spec **spec, char **target_params) 140062306a36Sopenharmony_ci{ 140162306a36Sopenharmony_ci static_assert(__alignof__(struct dm_target_spec) <= 8, 140262306a36Sopenharmony_ci "struct dm_target_spec must not require more than 8-byte alignment"); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci /* 140562306a36Sopenharmony_ci * Number of bytes remaining, starting with last. This is always 140662306a36Sopenharmony_ci * sizeof(struct dm_target_spec) or more, as otherwise *last was 140762306a36Sopenharmony_ci * out of bounds already. 140862306a36Sopenharmony_ci */ 140962306a36Sopenharmony_ci size_t remaining = end - (char *)last; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* 141262306a36Sopenharmony_ci * There must be room for both the next target spec and the 141362306a36Sopenharmony_ci * NUL-terminator of the target itself. 141462306a36Sopenharmony_ci */ 141562306a36Sopenharmony_ci if (remaining - sizeof(struct dm_target_spec) <= next) { 141662306a36Sopenharmony_ci DMERR("Target spec extends beyond end of parameters"); 141762306a36Sopenharmony_ci return -EINVAL; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (next % __alignof__(struct dm_target_spec)) { 142162306a36Sopenharmony_ci DMERR("Next dm_target_spec (offset %u) is not %zu-byte aligned", 142262306a36Sopenharmony_ci next, __alignof__(struct dm_target_spec)); 142362306a36Sopenharmony_ci return -EINVAL; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci *spec = (struct dm_target_spec *) ((unsigned char *) last + next); 142762306a36Sopenharmony_ci *target_params = (char *) (*spec + 1); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci return 0; 143062306a36Sopenharmony_ci} 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_cistatic int populate_table(struct dm_table *table, 143362306a36Sopenharmony_ci struct dm_ioctl *param, size_t param_size) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci int r; 143662306a36Sopenharmony_ci unsigned int i = 0; 143762306a36Sopenharmony_ci struct dm_target_spec *spec = (struct dm_target_spec *) param; 143862306a36Sopenharmony_ci uint32_t next = param->data_start; 143962306a36Sopenharmony_ci const char *const end = (const char *) param + param_size; 144062306a36Sopenharmony_ci char *target_params; 144162306a36Sopenharmony_ci size_t min_size = sizeof(struct dm_ioctl); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (!param->target_count) { 144462306a36Sopenharmony_ci DMERR("%s: no targets specified", __func__); 144562306a36Sopenharmony_ci return -EINVAL; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci for (i = 0; i < param->target_count; i++) { 144962306a36Sopenharmony_ci const char *nul_terminator; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (next < min_size) { 145262306a36Sopenharmony_ci DMERR("%s: next target spec (offset %u) overlaps %s", 145362306a36Sopenharmony_ci __func__, next, i ? "previous target" : "'struct dm_ioctl'"); 145462306a36Sopenharmony_ci return -EINVAL; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci r = next_target(spec, next, end, &spec, &target_params); 145862306a36Sopenharmony_ci if (r) { 145962306a36Sopenharmony_ci DMERR("unable to find target"); 146062306a36Sopenharmony_ci return r; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci nul_terminator = memchr(target_params, 0, (size_t)(end - target_params)); 146462306a36Sopenharmony_ci if (nul_terminator == NULL) { 146562306a36Sopenharmony_ci DMERR("%s: target parameters not NUL-terminated", __func__); 146662306a36Sopenharmony_ci return -EINVAL; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci /* Add 1 for NUL terminator */ 147062306a36Sopenharmony_ci min_size = (size_t)(nul_terminator - (const char *)spec) + 1; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci r = dm_table_add_target(table, spec->target_type, 147362306a36Sopenharmony_ci (sector_t) spec->sector_start, 147462306a36Sopenharmony_ci (sector_t) spec->length, 147562306a36Sopenharmony_ci target_params); 147662306a36Sopenharmony_ci if (r) { 147762306a36Sopenharmony_ci DMERR("error adding target to table"); 147862306a36Sopenharmony_ci return r; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci next = spec->next; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci return dm_table_complete(table); 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci if (cur == new || 149062306a36Sopenharmony_ci (cur == DM_TYPE_BIO_BASED && new == DM_TYPE_DAX_BIO_BASED)) 149162306a36Sopenharmony_ci return true; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci return false; 149462306a36Sopenharmony_ci} 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci int r; 149962306a36Sopenharmony_ci struct hash_cell *hc; 150062306a36Sopenharmony_ci struct dm_table *t, *old_map = NULL; 150162306a36Sopenharmony_ci struct mapped_device *md; 150262306a36Sopenharmony_ci struct target_type *immutable_target_type; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci md = find_device(param); 150562306a36Sopenharmony_ci if (!md) 150662306a36Sopenharmony_ci return -ENXIO; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci r = dm_table_create(&t, get_mode(param), param->target_count, md); 150962306a36Sopenharmony_ci if (r) 151062306a36Sopenharmony_ci goto err; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* Protect md->type and md->queue against concurrent table loads. */ 151362306a36Sopenharmony_ci dm_lock_md_type(md); 151462306a36Sopenharmony_ci r = populate_table(t, param, param_size); 151562306a36Sopenharmony_ci if (r) 151662306a36Sopenharmony_ci goto err_unlock_md_type; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci dm_ima_measure_on_table_load(t, STATUSTYPE_IMA); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci immutable_target_type = dm_get_immutable_target_type(md); 152162306a36Sopenharmony_ci if (immutable_target_type && 152262306a36Sopenharmony_ci (immutable_target_type != dm_table_get_immutable_target_type(t)) && 152362306a36Sopenharmony_ci !dm_table_get_wildcard_target(t)) { 152462306a36Sopenharmony_ci DMERR("can't replace immutable target type %s", 152562306a36Sopenharmony_ci immutable_target_type->name); 152662306a36Sopenharmony_ci r = -EINVAL; 152762306a36Sopenharmony_ci goto err_unlock_md_type; 152862306a36Sopenharmony_ci } 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci if (dm_get_md_type(md) == DM_TYPE_NONE) { 153162306a36Sopenharmony_ci /* setup md->queue to reflect md's type (may block) */ 153262306a36Sopenharmony_ci r = dm_setup_md_queue(md, t); 153362306a36Sopenharmony_ci if (r) { 153462306a36Sopenharmony_ci DMERR("unable to set up device queue for new table."); 153562306a36Sopenharmony_ci goto err_unlock_md_type; 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci } else if (!is_valid_type(dm_get_md_type(md), dm_table_get_type(t))) { 153862306a36Sopenharmony_ci DMERR("can't change device type (old=%u vs new=%u) after initial table load.", 153962306a36Sopenharmony_ci dm_get_md_type(md), dm_table_get_type(t)); 154062306a36Sopenharmony_ci r = -EINVAL; 154162306a36Sopenharmony_ci goto err_unlock_md_type; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci dm_unlock_md_type(md); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci /* stage inactive table */ 154762306a36Sopenharmony_ci down_write(&_hash_lock); 154862306a36Sopenharmony_ci hc = dm_get_mdptr(md); 154962306a36Sopenharmony_ci if (!hc) { 155062306a36Sopenharmony_ci DMERR("device has been removed from the dev hash table."); 155162306a36Sopenharmony_ci up_write(&_hash_lock); 155262306a36Sopenharmony_ci r = -ENXIO; 155362306a36Sopenharmony_ci goto err_destroy_table; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (hc->new_map) 155762306a36Sopenharmony_ci old_map = hc->new_map; 155862306a36Sopenharmony_ci hc->new_map = t; 155962306a36Sopenharmony_ci up_write(&_hash_lock); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci param->flags |= DM_INACTIVE_PRESENT_FLAG; 156262306a36Sopenharmony_ci __dev_status(md, param); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (old_map) { 156562306a36Sopenharmony_ci dm_sync_table(md); 156662306a36Sopenharmony_ci dm_table_destroy(old_map); 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci dm_put(md); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci return 0; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cierr_unlock_md_type: 157462306a36Sopenharmony_ci dm_unlock_md_type(md); 157562306a36Sopenharmony_cierr_destroy_table: 157662306a36Sopenharmony_ci dm_table_destroy(t); 157762306a36Sopenharmony_cierr: 157862306a36Sopenharmony_ci dm_put(md); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci return r; 158162306a36Sopenharmony_ci} 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_cistatic int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_size) 158462306a36Sopenharmony_ci{ 158562306a36Sopenharmony_ci struct hash_cell *hc; 158662306a36Sopenharmony_ci struct mapped_device *md; 158762306a36Sopenharmony_ci struct dm_table *old_map = NULL; 158862306a36Sopenharmony_ci bool has_new_map = false; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci down_write(&_hash_lock); 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci hc = __find_device_hash_cell(param); 159362306a36Sopenharmony_ci if (!hc) { 159462306a36Sopenharmony_ci DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table."); 159562306a36Sopenharmony_ci up_write(&_hash_lock); 159662306a36Sopenharmony_ci return -ENXIO; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (hc->new_map) { 160062306a36Sopenharmony_ci old_map = hc->new_map; 160162306a36Sopenharmony_ci hc->new_map = NULL; 160262306a36Sopenharmony_ci has_new_map = true; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci md = hc->md; 160662306a36Sopenharmony_ci up_write(&_hash_lock); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci param->flags &= ~DM_INACTIVE_PRESENT_FLAG; 160962306a36Sopenharmony_ci __dev_status(md, param); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci if (old_map) { 161262306a36Sopenharmony_ci dm_sync_table(md); 161362306a36Sopenharmony_ci dm_table_destroy(old_map); 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci dm_ima_measure_on_table_clear(md, has_new_map); 161662306a36Sopenharmony_ci dm_put(md); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci return 0; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci/* 162262306a36Sopenharmony_ci * Retrieves a list of devices used by a particular dm device. 162362306a36Sopenharmony_ci */ 162462306a36Sopenharmony_cistatic void retrieve_deps(struct dm_table *table, 162562306a36Sopenharmony_ci struct dm_ioctl *param, size_t param_size) 162662306a36Sopenharmony_ci{ 162762306a36Sopenharmony_ci unsigned int count = 0; 162862306a36Sopenharmony_ci struct list_head *tmp; 162962306a36Sopenharmony_ci size_t len, needed; 163062306a36Sopenharmony_ci struct dm_dev_internal *dd; 163162306a36Sopenharmony_ci struct dm_target_deps *deps; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci down_read(&table->devices_lock); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci deps = get_result_buffer(param, param_size, &len); 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci /* 163862306a36Sopenharmony_ci * Count the devices. 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci list_for_each(tmp, dm_table_get_devices(table)) 164162306a36Sopenharmony_ci count++; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci /* 164462306a36Sopenharmony_ci * Check we have enough space. 164562306a36Sopenharmony_ci */ 164662306a36Sopenharmony_ci needed = struct_size(deps, dev, count); 164762306a36Sopenharmony_ci if (len < needed) { 164862306a36Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 164962306a36Sopenharmony_ci goto out; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci /* 165362306a36Sopenharmony_ci * Fill in the devices. 165462306a36Sopenharmony_ci */ 165562306a36Sopenharmony_ci deps->count = count; 165662306a36Sopenharmony_ci count = 0; 165762306a36Sopenharmony_ci list_for_each_entry(dd, dm_table_get_devices(table), list) 165862306a36Sopenharmony_ci deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci param->data_size = param->data_start + needed; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ciout: 166362306a36Sopenharmony_ci up_read(&table->devices_lock); 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_cistatic int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci struct mapped_device *md; 166962306a36Sopenharmony_ci struct dm_table *table; 167062306a36Sopenharmony_ci int srcu_idx; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci md = find_device(param); 167362306a36Sopenharmony_ci if (!md) 167462306a36Sopenharmony_ci return -ENXIO; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci __dev_status(md, param); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci table = dm_get_live_or_inactive_table(md, param, &srcu_idx); 167962306a36Sopenharmony_ci if (table) 168062306a36Sopenharmony_ci retrieve_deps(table, param, param_size); 168162306a36Sopenharmony_ci dm_put_live_table(md, srcu_idx); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci dm_put(md); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci return 0; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci/* 168962306a36Sopenharmony_ci * Return the status of a device as a text string for each 169062306a36Sopenharmony_ci * target. 169162306a36Sopenharmony_ci */ 169262306a36Sopenharmony_cistatic int table_status(struct file *filp, struct dm_ioctl *param, size_t param_size) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci struct mapped_device *md; 169562306a36Sopenharmony_ci struct dm_table *table; 169662306a36Sopenharmony_ci int srcu_idx; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci md = find_device(param); 169962306a36Sopenharmony_ci if (!md) 170062306a36Sopenharmony_ci return -ENXIO; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci __dev_status(md, param); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci table = dm_get_live_or_inactive_table(md, param, &srcu_idx); 170562306a36Sopenharmony_ci if (table) 170662306a36Sopenharmony_ci retrieve_status(table, param, param_size); 170762306a36Sopenharmony_ci dm_put_live_table(md, srcu_idx); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci dm_put(md); 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci return 0; 171262306a36Sopenharmony_ci} 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci/* 171562306a36Sopenharmony_ci * Process device-mapper dependent messages. Messages prefixed with '@' 171662306a36Sopenharmony_ci * are processed by the DM core. All others are delivered to the target. 171762306a36Sopenharmony_ci * Returns a number <= 1 if message was processed by device mapper. 171862306a36Sopenharmony_ci * Returns 2 if message should be delivered to the target. 171962306a36Sopenharmony_ci */ 172062306a36Sopenharmony_cistatic int message_for_md(struct mapped_device *md, unsigned int argc, char **argv, 172162306a36Sopenharmony_ci char *result, unsigned int maxlen) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci int r; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci if (**argv != '@') 172662306a36Sopenharmony_ci return 2; /* no '@' prefix, deliver to target */ 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci if (!strcasecmp(argv[0], "@cancel_deferred_remove")) { 172962306a36Sopenharmony_ci if (argc != 1) { 173062306a36Sopenharmony_ci DMERR("Invalid arguments for @cancel_deferred_remove"); 173162306a36Sopenharmony_ci return -EINVAL; 173262306a36Sopenharmony_ci } 173362306a36Sopenharmony_ci return dm_cancel_deferred_remove(md); 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci r = dm_stats_message(md, argc, argv, result, maxlen); 173762306a36Sopenharmony_ci if (r < 2) 173862306a36Sopenharmony_ci return r; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci DMERR("Unsupported message sent to DM core: %s", argv[0]); 174162306a36Sopenharmony_ci return -EINVAL; 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci/* 174562306a36Sopenharmony_ci * Pass a message to the target that's at the supplied device offset. 174662306a36Sopenharmony_ci */ 174762306a36Sopenharmony_cistatic int target_message(struct file *filp, struct dm_ioctl *param, size_t param_size) 174862306a36Sopenharmony_ci{ 174962306a36Sopenharmony_ci int r, argc; 175062306a36Sopenharmony_ci char **argv; 175162306a36Sopenharmony_ci struct mapped_device *md; 175262306a36Sopenharmony_ci struct dm_table *table; 175362306a36Sopenharmony_ci struct dm_target *ti; 175462306a36Sopenharmony_ci struct dm_target_msg *tmsg = (void *) param + param->data_start; 175562306a36Sopenharmony_ci size_t maxlen; 175662306a36Sopenharmony_ci char *result = get_result_buffer(param, param_size, &maxlen); 175762306a36Sopenharmony_ci int srcu_idx; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci md = find_device(param); 176062306a36Sopenharmony_ci if (!md) 176162306a36Sopenharmony_ci return -ENXIO; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci if (tmsg < (struct dm_target_msg *) param->data || 176462306a36Sopenharmony_ci invalid_str(tmsg->message, (void *) param + param_size)) { 176562306a36Sopenharmony_ci DMERR("Invalid target message parameters."); 176662306a36Sopenharmony_ci r = -EINVAL; 176762306a36Sopenharmony_ci goto out; 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci r = dm_split_args(&argc, &argv, tmsg->message); 177162306a36Sopenharmony_ci if (r) { 177262306a36Sopenharmony_ci DMERR("Failed to split target message parameters"); 177362306a36Sopenharmony_ci goto out; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci if (!argc) { 177762306a36Sopenharmony_ci DMERR("Empty message received."); 177862306a36Sopenharmony_ci r = -EINVAL; 177962306a36Sopenharmony_ci goto out_argv; 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci r = message_for_md(md, argc, argv, result, maxlen); 178362306a36Sopenharmony_ci if (r <= 1) 178462306a36Sopenharmony_ci goto out_argv; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci table = dm_get_live_table(md, &srcu_idx); 178762306a36Sopenharmony_ci if (!table) 178862306a36Sopenharmony_ci goto out_table; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (dm_deleting_md(md)) { 179162306a36Sopenharmony_ci r = -ENXIO; 179262306a36Sopenharmony_ci goto out_table; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci ti = dm_table_find_target(table, tmsg->sector); 179662306a36Sopenharmony_ci if (!ti) { 179762306a36Sopenharmony_ci DMERR("Target message sector outside device."); 179862306a36Sopenharmony_ci r = -EINVAL; 179962306a36Sopenharmony_ci } else if (ti->type->message) 180062306a36Sopenharmony_ci r = ti->type->message(ti, argc, argv, result, maxlen); 180162306a36Sopenharmony_ci else { 180262306a36Sopenharmony_ci DMERR("Target type does not support messages"); 180362306a36Sopenharmony_ci r = -EINVAL; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci out_table: 180762306a36Sopenharmony_ci dm_put_live_table(md, srcu_idx); 180862306a36Sopenharmony_ci out_argv: 180962306a36Sopenharmony_ci kfree(argv); 181062306a36Sopenharmony_ci out: 181162306a36Sopenharmony_ci if (r >= 0) 181262306a36Sopenharmony_ci __dev_status(md, param); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci if (r == 1) { 181562306a36Sopenharmony_ci param->flags |= DM_DATA_OUT_FLAG; 181662306a36Sopenharmony_ci if (dm_message_test_buffer_overflow(result, maxlen)) 181762306a36Sopenharmony_ci param->flags |= DM_BUFFER_FULL_FLAG; 181862306a36Sopenharmony_ci else 181962306a36Sopenharmony_ci param->data_size = param->data_start + strlen(result) + 1; 182062306a36Sopenharmony_ci r = 0; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci dm_put(md); 182462306a36Sopenharmony_ci return r; 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci/* 182862306a36Sopenharmony_ci * The ioctl parameter block consists of two parts, a dm_ioctl struct 182962306a36Sopenharmony_ci * followed by a data buffer. This flag is set if the second part, 183062306a36Sopenharmony_ci * which has a variable size, is not used by the function processing 183162306a36Sopenharmony_ci * the ioctl. 183262306a36Sopenharmony_ci */ 183362306a36Sopenharmony_ci#define IOCTL_FLAGS_NO_PARAMS 1 183462306a36Sopenharmony_ci#define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT 2 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci/* 183762306a36Sopenharmony_ci *--------------------------------------------------------------- 183862306a36Sopenharmony_ci * Implementation of open/close/ioctl on the special char device. 183962306a36Sopenharmony_ci *--------------------------------------------------------------- 184062306a36Sopenharmony_ci */ 184162306a36Sopenharmony_cistatic ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci static const struct { 184462306a36Sopenharmony_ci int cmd; 184562306a36Sopenharmony_ci int flags; 184662306a36Sopenharmony_ci ioctl_fn fn; 184762306a36Sopenharmony_ci } _ioctls[] = { 184862306a36Sopenharmony_ci {DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */ 184962306a36Sopenharmony_ci {DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all}, 185062306a36Sopenharmony_ci {DM_LIST_DEVICES_CMD, 0, list_devices}, 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create}, 185362306a36Sopenharmony_ci {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove}, 185462306a36Sopenharmony_ci {DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename}, 185562306a36Sopenharmony_ci {DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend}, 185662306a36Sopenharmony_ci {DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status}, 185762306a36Sopenharmony_ci {DM_DEV_WAIT_CMD, 0, dev_wait}, 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci {DM_TABLE_LOAD_CMD, 0, table_load}, 186062306a36Sopenharmony_ci {DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear}, 186162306a36Sopenharmony_ci {DM_TABLE_DEPS_CMD, 0, table_deps}, 186262306a36Sopenharmony_ci {DM_TABLE_STATUS_CMD, 0, table_status}, 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci {DM_LIST_VERSIONS_CMD, 0, list_versions}, 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci {DM_TARGET_MSG_CMD, 0, target_message}, 186762306a36Sopenharmony_ci {DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry}, 186862306a36Sopenharmony_ci {DM_DEV_ARM_POLL_CMD, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll}, 186962306a36Sopenharmony_ci {DM_GET_TARGET_VERSION_CMD, 0, get_target_version}, 187062306a36Sopenharmony_ci }; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci if (unlikely(cmd >= ARRAY_SIZE(_ioctls))) 187362306a36Sopenharmony_ci return NULL; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci cmd = array_index_nospec(cmd, ARRAY_SIZE(_ioctls)); 187662306a36Sopenharmony_ci *ioctl_flags = _ioctls[cmd].flags; 187762306a36Sopenharmony_ci return _ioctls[cmd].fn; 187862306a36Sopenharmony_ci} 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci/* 188162306a36Sopenharmony_ci * As well as checking the version compatibility this always 188262306a36Sopenharmony_ci * copies the kernel interface version out. 188362306a36Sopenharmony_ci */ 188462306a36Sopenharmony_cistatic int check_version(unsigned int cmd, struct dm_ioctl __user *user, 188562306a36Sopenharmony_ci struct dm_ioctl *kernel_params) 188662306a36Sopenharmony_ci{ 188762306a36Sopenharmony_ci int r = 0; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci /* Make certain version is first member of dm_ioctl struct */ 189062306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct dm_ioctl, version) != 0); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci if (copy_from_user(kernel_params->version, user->version, sizeof(kernel_params->version))) 189362306a36Sopenharmony_ci return -EFAULT; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci if ((kernel_params->version[0] != DM_VERSION_MAJOR) || 189662306a36Sopenharmony_ci (kernel_params->version[1] > DM_VERSION_MINOR)) { 189762306a36Sopenharmony_ci DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", 189862306a36Sopenharmony_ci DM_VERSION_MAJOR, DM_VERSION_MINOR, 189962306a36Sopenharmony_ci DM_VERSION_PATCHLEVEL, 190062306a36Sopenharmony_ci kernel_params->version[0], 190162306a36Sopenharmony_ci kernel_params->version[1], 190262306a36Sopenharmony_ci kernel_params->version[2], 190362306a36Sopenharmony_ci cmd); 190462306a36Sopenharmony_ci r = -EINVAL; 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci /* 190862306a36Sopenharmony_ci * Fill in the kernel version. 190962306a36Sopenharmony_ci */ 191062306a36Sopenharmony_ci kernel_params->version[0] = DM_VERSION_MAJOR; 191162306a36Sopenharmony_ci kernel_params->version[1] = DM_VERSION_MINOR; 191262306a36Sopenharmony_ci kernel_params->version[2] = DM_VERSION_PATCHLEVEL; 191362306a36Sopenharmony_ci if (copy_to_user(user->version, kernel_params->version, sizeof(kernel_params->version))) 191462306a36Sopenharmony_ci return -EFAULT; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci return r; 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci#define DM_PARAMS_MALLOC 0x0001 /* Params allocated with kvmalloc() */ 192062306a36Sopenharmony_ci#define DM_WIPE_BUFFER 0x0010 /* Wipe input buffer before returning from ioctl */ 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_cistatic void free_params(struct dm_ioctl *param, size_t param_size, int param_flags) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci if (param_flags & DM_WIPE_BUFFER) 192562306a36Sopenharmony_ci memset(param, 0, param_size); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (param_flags & DM_PARAMS_MALLOC) 192862306a36Sopenharmony_ci kvfree(param); 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_cistatic int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel, 193262306a36Sopenharmony_ci int ioctl_flags, struct dm_ioctl **param, int *param_flags) 193362306a36Sopenharmony_ci{ 193462306a36Sopenharmony_ci struct dm_ioctl *dmi; 193562306a36Sopenharmony_ci int secure_data; 193662306a36Sopenharmony_ci const size_t minimum_data_size = offsetof(struct dm_ioctl, data); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci /* check_version() already copied version from userspace, avoid TOCTOU */ 193962306a36Sopenharmony_ci if (copy_from_user((char *)param_kernel + sizeof(param_kernel->version), 194062306a36Sopenharmony_ci (char __user *)user + sizeof(param_kernel->version), 194162306a36Sopenharmony_ci minimum_data_size - sizeof(param_kernel->version))) 194262306a36Sopenharmony_ci return -EFAULT; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci if (unlikely(param_kernel->data_size < minimum_data_size) || 194562306a36Sopenharmony_ci unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) { 194662306a36Sopenharmony_ci DMERR("Invalid data size in the ioctl structure: %u", 194762306a36Sopenharmony_ci param_kernel->data_size); 194862306a36Sopenharmony_ci return -EINVAL; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci *param_flags = secure_data ? DM_WIPE_BUFFER : 0; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) { 195662306a36Sopenharmony_ci dmi = param_kernel; 195762306a36Sopenharmony_ci dmi->data_size = minimum_data_size; 195862306a36Sopenharmony_ci goto data_copied; 195962306a36Sopenharmony_ci } 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci /* 196262306a36Sopenharmony_ci * Use __GFP_HIGH to avoid low memory issues when a device is 196362306a36Sopenharmony_ci * suspended and the ioctl is needed to resume it. 196462306a36Sopenharmony_ci * Use kmalloc() rather than vmalloc() when we can. 196562306a36Sopenharmony_ci */ 196662306a36Sopenharmony_ci dmi = NULL; 196762306a36Sopenharmony_ci dmi = kvmalloc(param_kernel->data_size, GFP_NOIO | __GFP_HIGH); 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci if (!dmi) { 197062306a36Sopenharmony_ci if (secure_data && clear_user(user, param_kernel->data_size)) 197162306a36Sopenharmony_ci return -EFAULT; 197262306a36Sopenharmony_ci return -ENOMEM; 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci *param_flags |= DM_PARAMS_MALLOC; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci /* Copy from param_kernel (which was already copied from user) */ 197862306a36Sopenharmony_ci memcpy(dmi, param_kernel, minimum_data_size); 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci if (copy_from_user(&dmi->data, (char __user *)user + minimum_data_size, 198162306a36Sopenharmony_ci param_kernel->data_size - minimum_data_size)) 198262306a36Sopenharmony_ci goto bad; 198362306a36Sopenharmony_cidata_copied: 198462306a36Sopenharmony_ci /* Wipe the user buffer so we do not return it to userspace */ 198562306a36Sopenharmony_ci if (secure_data && clear_user(user, param_kernel->data_size)) 198662306a36Sopenharmony_ci goto bad; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci *param = dmi; 198962306a36Sopenharmony_ci return 0; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_cibad: 199262306a36Sopenharmony_ci free_params(dmi, param_kernel->data_size, *param_flags); 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci return -EFAULT; 199562306a36Sopenharmony_ci} 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_cistatic int validate_params(uint cmd, struct dm_ioctl *param) 199862306a36Sopenharmony_ci{ 199962306a36Sopenharmony_ci /* Always clear this flag */ 200062306a36Sopenharmony_ci param->flags &= ~DM_BUFFER_FULL_FLAG; 200162306a36Sopenharmony_ci param->flags &= ~DM_UEVENT_GENERATED_FLAG; 200262306a36Sopenharmony_ci param->flags &= ~DM_SECURE_DATA_FLAG; 200362306a36Sopenharmony_ci param->flags &= ~DM_DATA_OUT_FLAG; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci /* Ignores parameters */ 200662306a36Sopenharmony_ci if (cmd == DM_REMOVE_ALL_CMD || 200762306a36Sopenharmony_ci cmd == DM_LIST_DEVICES_CMD || 200862306a36Sopenharmony_ci cmd == DM_LIST_VERSIONS_CMD) 200962306a36Sopenharmony_ci return 0; 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci if (cmd == DM_DEV_CREATE_CMD) { 201262306a36Sopenharmony_ci if (!*param->name) { 201362306a36Sopenharmony_ci DMERR("name not supplied when creating device"); 201462306a36Sopenharmony_ci return -EINVAL; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci } else if (*param->uuid && *param->name) { 201762306a36Sopenharmony_ci DMERR("only supply one of name or uuid, cmd(%u)", cmd); 201862306a36Sopenharmony_ci return -EINVAL; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci /* Ensure strings are terminated */ 202262306a36Sopenharmony_ci param->name[DM_NAME_LEN - 1] = '\0'; 202362306a36Sopenharmony_ci param->uuid[DM_UUID_LEN - 1] = '\0'; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci return 0; 202662306a36Sopenharmony_ci} 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_cistatic int ctl_ioctl(struct file *file, uint command, struct dm_ioctl __user *user) 202962306a36Sopenharmony_ci{ 203062306a36Sopenharmony_ci int r = 0; 203162306a36Sopenharmony_ci int ioctl_flags; 203262306a36Sopenharmony_ci int param_flags; 203362306a36Sopenharmony_ci unsigned int cmd; 203462306a36Sopenharmony_ci struct dm_ioctl *param; 203562306a36Sopenharmony_ci ioctl_fn fn = NULL; 203662306a36Sopenharmony_ci size_t input_param_size; 203762306a36Sopenharmony_ci struct dm_ioctl param_kernel; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci /* only root can play with this */ 204062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 204162306a36Sopenharmony_ci return -EACCES; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci if (_IOC_TYPE(command) != DM_IOCTL) 204462306a36Sopenharmony_ci return -ENOTTY; 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci cmd = _IOC_NR(command); 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci /* 204962306a36Sopenharmony_ci * Check the interface version passed in. This also 205062306a36Sopenharmony_ci * writes out the kernel's interface version. 205162306a36Sopenharmony_ci */ 205262306a36Sopenharmony_ci r = check_version(cmd, user, ¶m_kernel); 205362306a36Sopenharmony_ci if (r) 205462306a36Sopenharmony_ci return r; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci /* 205762306a36Sopenharmony_ci * Nothing more to do for the version command. 205862306a36Sopenharmony_ci */ 205962306a36Sopenharmony_ci if (cmd == DM_VERSION_CMD) 206062306a36Sopenharmony_ci return 0; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci fn = lookup_ioctl(cmd, &ioctl_flags); 206362306a36Sopenharmony_ci if (!fn) { 206462306a36Sopenharmony_ci DMERR("dm_ctl_ioctl: unknown command 0x%x", command); 206562306a36Sopenharmony_ci return -ENOTTY; 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci /* 206962306a36Sopenharmony_ci * Copy the parameters into kernel space. 207062306a36Sopenharmony_ci */ 207162306a36Sopenharmony_ci r = copy_params(user, ¶m_kernel, ioctl_flags, ¶m, ¶m_flags); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci if (r) 207462306a36Sopenharmony_ci return r; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci input_param_size = param->data_size; 207762306a36Sopenharmony_ci r = validate_params(cmd, param); 207862306a36Sopenharmony_ci if (r) 207962306a36Sopenharmony_ci goto out; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci param->data_size = offsetof(struct dm_ioctl, data); 208262306a36Sopenharmony_ci r = fn(file, param, input_param_size); 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (unlikely(param->flags & DM_BUFFER_FULL_FLAG) && 208562306a36Sopenharmony_ci unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS)) 208662306a36Sopenharmony_ci DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci if (!r && ioctl_flags & IOCTL_FLAGS_ISSUE_GLOBAL_EVENT) 208962306a36Sopenharmony_ci dm_issue_global_event(); 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci /* 209262306a36Sopenharmony_ci * Copy the results back to userland. 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_ci if (!r && copy_to_user(user, param, param->data_size)) 209562306a36Sopenharmony_ci r = -EFAULT; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ciout: 209862306a36Sopenharmony_ci free_params(param, input_param_size, param_flags); 209962306a36Sopenharmony_ci return r; 210062306a36Sopenharmony_ci} 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_cistatic long dm_ctl_ioctl(struct file *file, uint command, ulong u) 210362306a36Sopenharmony_ci{ 210462306a36Sopenharmony_ci return (long)ctl_ioctl(file, command, (struct dm_ioctl __user *)u); 210562306a36Sopenharmony_ci} 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 210862306a36Sopenharmony_cistatic long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u)); 211162306a36Sopenharmony_ci} 211262306a36Sopenharmony_ci#else 211362306a36Sopenharmony_ci#define dm_compat_ctl_ioctl NULL 211462306a36Sopenharmony_ci#endif 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_cistatic int dm_open(struct inode *inode, struct file *filp) 211762306a36Sopenharmony_ci{ 211862306a36Sopenharmony_ci int r; 211962306a36Sopenharmony_ci struct dm_file *priv; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci r = nonseekable_open(inode, filp); 212262306a36Sopenharmony_ci if (unlikely(r)) 212362306a36Sopenharmony_ci return r; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci priv = filp->private_data = kmalloc(sizeof(struct dm_file), GFP_KERNEL); 212662306a36Sopenharmony_ci if (!priv) 212762306a36Sopenharmony_ci return -ENOMEM; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci priv->global_event_nr = atomic_read(&dm_global_event_nr); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci return 0; 213262306a36Sopenharmony_ci} 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_cistatic int dm_release(struct inode *inode, struct file *filp) 213562306a36Sopenharmony_ci{ 213662306a36Sopenharmony_ci kfree(filp->private_data); 213762306a36Sopenharmony_ci return 0; 213862306a36Sopenharmony_ci} 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_cistatic __poll_t dm_poll(struct file *filp, poll_table *wait) 214162306a36Sopenharmony_ci{ 214262306a36Sopenharmony_ci struct dm_file *priv = filp->private_data; 214362306a36Sopenharmony_ci __poll_t mask = 0; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci poll_wait(filp, &dm_global_eventq, wait); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci if ((int)(atomic_read(&dm_global_event_nr) - priv->global_event_nr) > 0) 214862306a36Sopenharmony_ci mask |= EPOLLIN; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci return mask; 215162306a36Sopenharmony_ci} 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_cistatic const struct file_operations _ctl_fops = { 215462306a36Sopenharmony_ci .open = dm_open, 215562306a36Sopenharmony_ci .release = dm_release, 215662306a36Sopenharmony_ci .poll = dm_poll, 215762306a36Sopenharmony_ci .unlocked_ioctl = dm_ctl_ioctl, 215862306a36Sopenharmony_ci .compat_ioctl = dm_compat_ctl_ioctl, 215962306a36Sopenharmony_ci .owner = THIS_MODULE, 216062306a36Sopenharmony_ci .llseek = noop_llseek, 216162306a36Sopenharmony_ci}; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cistatic struct miscdevice _dm_misc = { 216462306a36Sopenharmony_ci .minor = MAPPER_CTRL_MINOR, 216562306a36Sopenharmony_ci .name = DM_NAME, 216662306a36Sopenharmony_ci .nodename = DM_DIR "/" DM_CONTROL_NODE, 216762306a36Sopenharmony_ci .fops = &_ctl_fops 216862306a36Sopenharmony_ci}; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ciMODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR); 217162306a36Sopenharmony_ciMODULE_ALIAS("devname:" DM_DIR "/" DM_CONTROL_NODE); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci/* 217462306a36Sopenharmony_ci * Create misc character device and link to DM_DIR/control. 217562306a36Sopenharmony_ci */ 217662306a36Sopenharmony_ciint __init dm_interface_init(void) 217762306a36Sopenharmony_ci{ 217862306a36Sopenharmony_ci int r; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci r = misc_register(&_dm_misc); 218162306a36Sopenharmony_ci if (r) { 218262306a36Sopenharmony_ci DMERR("misc_register failed for control device"); 218362306a36Sopenharmony_ci return r; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR, 218762306a36Sopenharmony_ci DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA, 218862306a36Sopenharmony_ci DM_DRIVER_EMAIL); 218962306a36Sopenharmony_ci return 0; 219062306a36Sopenharmony_ci} 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_civoid dm_interface_exit(void) 219362306a36Sopenharmony_ci{ 219462306a36Sopenharmony_ci misc_deregister(&_dm_misc); 219562306a36Sopenharmony_ci dm_hash_exit(); 219662306a36Sopenharmony_ci} 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci/** 219962306a36Sopenharmony_ci * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers 220062306a36Sopenharmony_ci * @md: Pointer to mapped_device 220162306a36Sopenharmony_ci * @name: Buffer (size DM_NAME_LEN) for name 220262306a36Sopenharmony_ci * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined 220362306a36Sopenharmony_ci */ 220462306a36Sopenharmony_ciint dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) 220562306a36Sopenharmony_ci{ 220662306a36Sopenharmony_ci int r = 0; 220762306a36Sopenharmony_ci struct hash_cell *hc; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci if (!md) 221062306a36Sopenharmony_ci return -ENXIO; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci mutex_lock(&dm_hash_cells_mutex); 221362306a36Sopenharmony_ci hc = dm_get_mdptr(md); 221462306a36Sopenharmony_ci if (!hc) { 221562306a36Sopenharmony_ci r = -ENXIO; 221662306a36Sopenharmony_ci goto out; 221762306a36Sopenharmony_ci } 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci if (name) 222062306a36Sopenharmony_ci strcpy(name, hc->name); 222162306a36Sopenharmony_ci if (uuid) 222262306a36Sopenharmony_ci strcpy(uuid, hc->uuid ? : ""); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ciout: 222562306a36Sopenharmony_ci mutex_unlock(&dm_hash_cells_mutex); 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci return r; 222862306a36Sopenharmony_ci} 222962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_copy_name_and_uuid); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci/** 223262306a36Sopenharmony_ci * dm_early_create - create a mapped device in early boot. 223362306a36Sopenharmony_ci * 223462306a36Sopenharmony_ci * @dmi: Contains main information of the device mapping to be created. 223562306a36Sopenharmony_ci * @spec_array: array of pointers to struct dm_target_spec. Describes the 223662306a36Sopenharmony_ci * mapping table of the device. 223762306a36Sopenharmony_ci * @target_params_array: array of strings with the parameters to a specific 223862306a36Sopenharmony_ci * target. 223962306a36Sopenharmony_ci * 224062306a36Sopenharmony_ci * Instead of having the struct dm_target_spec and the parameters for every 224162306a36Sopenharmony_ci * target embedded at the end of struct dm_ioctl (as performed in a normal 224262306a36Sopenharmony_ci * ioctl), pass them as arguments, so the caller doesn't need to serialize them. 224362306a36Sopenharmony_ci * The size of the spec_array and target_params_array is given by 224462306a36Sopenharmony_ci * @dmi->target_count. 224562306a36Sopenharmony_ci * This function is supposed to be called in early boot, so locking mechanisms 224662306a36Sopenharmony_ci * to protect against concurrent loads are not required. 224762306a36Sopenharmony_ci */ 224862306a36Sopenharmony_ciint __init dm_early_create(struct dm_ioctl *dmi, 224962306a36Sopenharmony_ci struct dm_target_spec **spec_array, 225062306a36Sopenharmony_ci char **target_params_array) 225162306a36Sopenharmony_ci{ 225262306a36Sopenharmony_ci int r, m = DM_ANY_MINOR; 225362306a36Sopenharmony_ci struct dm_table *t, *old_map; 225462306a36Sopenharmony_ci struct mapped_device *md; 225562306a36Sopenharmony_ci unsigned int i; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if (!dmi->target_count) 225862306a36Sopenharmony_ci return -EINVAL; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci r = check_name(dmi->name); 226162306a36Sopenharmony_ci if (r) 226262306a36Sopenharmony_ci return r; 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci if (dmi->flags & DM_PERSISTENT_DEV_FLAG) 226562306a36Sopenharmony_ci m = MINOR(huge_decode_dev(dmi->dev)); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci /* alloc dm device */ 226862306a36Sopenharmony_ci r = dm_create(m, &md); 226962306a36Sopenharmony_ci if (r) 227062306a36Sopenharmony_ci return r; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci /* hash insert */ 227362306a36Sopenharmony_ci r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md); 227462306a36Sopenharmony_ci if (r) 227562306a36Sopenharmony_ci goto err_destroy_dm; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci /* alloc table */ 227862306a36Sopenharmony_ci r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md); 227962306a36Sopenharmony_ci if (r) 228062306a36Sopenharmony_ci goto err_hash_remove; 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci /* add targets */ 228362306a36Sopenharmony_ci for (i = 0; i < dmi->target_count; i++) { 228462306a36Sopenharmony_ci r = dm_table_add_target(t, spec_array[i]->target_type, 228562306a36Sopenharmony_ci (sector_t) spec_array[i]->sector_start, 228662306a36Sopenharmony_ci (sector_t) spec_array[i]->length, 228762306a36Sopenharmony_ci target_params_array[i]); 228862306a36Sopenharmony_ci if (r) { 228962306a36Sopenharmony_ci DMERR("error adding target to table"); 229062306a36Sopenharmony_ci goto err_destroy_table; 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci /* finish table */ 229562306a36Sopenharmony_ci r = dm_table_complete(t); 229662306a36Sopenharmony_ci if (r) 229762306a36Sopenharmony_ci goto err_destroy_table; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci /* setup md->queue to reflect md's type (may block) */ 230062306a36Sopenharmony_ci r = dm_setup_md_queue(md, t); 230162306a36Sopenharmony_ci if (r) { 230262306a36Sopenharmony_ci DMERR("unable to set up device queue for new table."); 230362306a36Sopenharmony_ci goto err_destroy_table; 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci /* Set new map */ 230762306a36Sopenharmony_ci dm_suspend(md, 0); 230862306a36Sopenharmony_ci old_map = dm_swap_table(md, t); 230962306a36Sopenharmony_ci if (IS_ERR(old_map)) { 231062306a36Sopenharmony_ci r = PTR_ERR(old_map); 231162306a36Sopenharmony_ci goto err_destroy_table; 231262306a36Sopenharmony_ci } 231362306a36Sopenharmony_ci set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG)); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci /* resume device */ 231662306a36Sopenharmony_ci r = dm_resume(md); 231762306a36Sopenharmony_ci if (r) 231862306a36Sopenharmony_ci goto err_destroy_table; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name); 232162306a36Sopenharmony_ci dm_put(md); 232262306a36Sopenharmony_ci return 0; 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_cierr_destroy_table: 232562306a36Sopenharmony_ci dm_table_destroy(t); 232662306a36Sopenharmony_cierr_hash_remove: 232762306a36Sopenharmony_ci down_write(&_hash_lock); 232862306a36Sopenharmony_ci (void) __hash_remove(__get_name_cell(dmi->name)); 232962306a36Sopenharmony_ci up_write(&_hash_lock); 233062306a36Sopenharmony_ci /* release reference from __get_name_cell */ 233162306a36Sopenharmony_ci dm_put(md); 233262306a36Sopenharmony_cierr_destroy_dm: 233362306a36Sopenharmony_ci dm_put(md); 233462306a36Sopenharmony_ci dm_destroy(md); 233562306a36Sopenharmony_ci return r; 233662306a36Sopenharmony_ci} 2337