// TODO add copyright #include #include #include #include #include #include #include #include #include #include #include #include // #define _BTRFS_DEVEL_ 1 #ifdef _BTRFS_DEVEL_ #include "ioctl.h" #endif #include "btrfs_utils.h" #include "btrfs_local.h" #include "btrfs_mgr.h" #include "btrfs_mtdt_store.h" /* * XXX:TEMP * Ideally the interface_priv should be invisible to the platform, * we are including this in order to get the definition of the * opaque types. If in future if & when we remove the opaque types we should * remove this include. */ #include #include "smagent/sma_interface_priv.h" using std::ios; static int btrfs_create_subvolume(const char *subvol_path); BtrfsMgr *BtrfsMgr::instance_; std::string BtrfsMgr::btrfs_mount_path_; BtrfsMgr::BtrfsMgr(std::string btrfs_mount_path) { // set the base vol path base_vol_path_ = btrfs_mount_path; mtdt_vol_path_ = base_vol_path_ + "/mtdt_vol"; snap_vol_path_ = base_vol_path_ + "/snap_vol"; vol_uuid_path_ = mtdt_vol_path_ + "/VOL_UUID"; vol_dir_ = "VOL"; snap_dir_ = "SNAP"; tag_dir_ = "TAG"; opaque_fn_ = "opaque"; // create the mtdt subvolume int res = btrfs_create_subvolume(mtdt_vol_path_.c_str()); if (res) { assert(0); } // create the snap_vol subvolume res = btrfs_create_subvolume(snap_vol_path_.c_str()); if (res) { assert(0); } // create the snap_vol subvolume res = btrfs_create_subvolume(vol_uuid_path_.c_str()); if (res) { assert(0); } mtds_ = new BtrfsMtdtStore(mtdt_vol_path_); /* Disable SIGPIPE interrupt so write's to closed sockets are handled through * EBROKENPIPE error instead. Not disabling the interrupt will raise SIGPIPE * and cause btrfs_smagent to exit. */ struct sigaction sa; bzero(&sa, sizeof(sa)); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; if(sigaction(SIGPIPE, &sa, 0) == -1) { std::cerr << "Couldn't set SIGPIPE action to SIG_IGN\n"; exit(1); } } // btrfs specific routines #ifdef _BTRFS_DEVEL_ static int btrfs_list_get_path_rootid(int fd, uint64_t *treeid) { int ret; struct btrfs_ioctl_ino_lookup_args args; memset(&args, 0, sizeof(args)); args.objectid = BTRFS_FIRST_FREE_OBJECTID; ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args); if (ret < 0) { BTRFS_ERR_PRINT("ERROR: can't perform the search - %s\n", strerror(errno)); return ret; } *treeid = args.treeid; return 0; } // local scope routine SmaOpaqueVolumeInfo * BtrfsMgr::btrfs_read_vol_info(void *vol_id, uint32_t type_no, SmaStatus *status) { SmaOpaqueVolumeInfo *vol_info = new SmaOpaqueVolumeInfo(); if (!vol_info) { *status = SMA_NOMEM; return NULL; } vol_info->op_ = SMA_INVALID; vol_info->opaque_size_ = 0; vol_info->opaque_ = NULL; BtrfsMgr *btrfs_mgr = BtrfsMgr::instance(); *status = btrfs_mgr->read_vol_info(vol_id, type_no, vol_info); if (*status == SMA_NOT_FOUND) { delete vol_info; vol_info = NULL; } return vol_info; } static int list_subvol_search(int fd, uint64_t top_id, std::vector &snap_name_list, std::vector &snap_id_list) { int ret; struct btrfs_ioctl_search_args args; struct btrfs_ioctl_search_key *sk = &args.key; struct btrfs_ioctl_search_header sh; unsigned long off = 0; int name_len; char *name; int i; memset(&args, 0, sizeof(args)); /* search in the tree of tree roots */ sk->tree_id = 1; /* * set the min and max to backref keys. The search will * only send back this type of key now. */ sk->max_type = BTRFS_ROOT_BACKREF_KEY; sk->min_type = BTRFS_ROOT_BACKREF_KEY; //sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID; sk->min_objectid = top_id + 1; /* * set all the other params to the max, we'll take any objectid * and any trans */ sk->max_objectid = BTRFS_LAST_FREE_OBJECTID; sk->max_offset = (uint64_t)-1; sk->max_transid = (uint64_t)-1; /* just a big number, doesn't matter much */ sk->nr_items = 4096; do { ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); if (ret < 0) { return ret; } off = 0; /* * for each item, pull the key out of the header and then * read the root_ref item it contains */ for (i = 0; i < sk->nr_items; i++) { memcpy(&sh, args.buf + off, sizeof(sh)); off += sizeof(sh); if (sh.type == BTRFS_ROOT_BACKREF_KEY) { struct btrfs_root_ref *ref; ref = (struct btrfs_root_ref *)(args.buf + off); name_len = btrfs_stack_root_ref_name_len(ref); name = (char *)(ref + 1); if (sh.offset == top_id) { std::string snap_name(name, name_len); snap_name_list.push_back(snap_name); uint64_t snap_id = sh.objectid; snap_id_list.push_back(snap_id); /* * Update the min ID to the last found * BTRFS_ROOT_BACKREF_KEY key. * Search results return objects in * increasing order with * other keys as well. Our min for the * the next search should be 1 greater * than the last found BACK_REF_KEY which * is basically the child of the top_id * (child for us is a snapshot). * Also, the sk->min_offset is the min * parent id. We might get search results * with higher offset. We should not set * this field with offset from any other * key. It will be fine if we dont set * anything as min_objectid will be * updated. * The min_type and max_type are already * set in the start, so no need to update * them as well. * Unless you really understand the search * algorithm btrfs uses, please avoid * any optimizations here. */ sk->min_objectid = sh.objectid; } } off += sh.len; } sk->min_objectid++; } while (sk->nr_items != 0); return 0; } #endif // ifdef _BTRFS_DEVEL_ // get the base subvolume path std::string BtrfsMgr::get_base_path() { return base_vol_path_; } // get absolute path for a given vol_id std::string BtrfsMgr::get_full_path(void *vol_id) { std::string vol_name((char *)vol_id); std::string full_path = this->get_base_path(); full_path += "/" + vol_name; return full_path; } // get the subvolume path std::string BtrfsMgr::get_mtdt_vol_path(void *vol_id) { // FIXME we are assuming that vol_id is less than 256 chars char str[MAX_VOLID_SIZE] = ""; strncpy(str, (char *)vol_id, MAX_VOLID_SIZE); std::string dir_path = mtdt_vol_path_; std::string dir_name(str); dir_path += "/" + dir_name; return dir_path; } // get the snap vol path std::string BtrfsMgr::get_snapvol_path(void *vol_id) { // FIXME we are assuming that vol_id is less than 256 chars char str[MAX_VOLID_SIZE] = ""; strncpy(str, (char *)vol_id, MAX_VOLID_SIZE); std::string dir_path = snap_vol_path_; std::string dir_name(str); dir_path += "/" + dir_name; return dir_path; } // check if the volume dir exists or not bool BtrfsMgr::is_dir_present(std::string dir_path) { DIR *pDir = NULL; bool present = false; std::string path = this->get_base_path(); path += "/" + dir_path; pDir = opendir(path.c_str()); if (pDir) { present = true; (void)closedir(pDir); } return present; } /* * BtrfsMgr::is_volume_offline * If the file /btrfs//offline is present, * the volume is offline else online. */ bool BtrfsMgr::is_volume_offline(std::string vol_name) { struct stat buffer; std::string path = this->get_base_path(); path += "/" + vol_name + "/offline"; return ((stat (path.c_str(), &buffer) == 0) && S_ISREG(buffer.st_mode)); } /* * BtrfsMgr::btrfs_update_volume_status * We are simulating volume is read_write else the volume * is DP. */ SmaStatus BtrfsMgr::btrfs_update_volume_status(std::string vol_name, SmaContainerStatus status) { struct stat buffer; std::string path = this->get_base_path(); path += "/" + vol_name + "/read_write"; SmaStatus st = SMA_OK; switch (status) { case SMA_CONTAINER_STATUS_NORMAL: { // Touch the file(even if it is present) fstream fs(path.c_str(), ios::out); fs.close(); BTRFS_PRINT("Updated volume status to normal.\n"); } break; case SMA_CONTAINER_STATUS_PROTECTED: { fstream fs(path.c_str(), ios::in); if (fs.is_open()) { fs.close(); remove(path.c_str()); BTRFS_PRINT("Updated volume status to protected.\n"); } } break; default: BTRFS_ERR_PRINT("Invalid update container status scenario.\n"); st = SMA_INTERNAL_ERROR; } return st; } /* * BtrfsMgr::is_rw_volume * If the file /btrfs//read_write is present, * we are simulating volume is read_write else the volume * is DP. */ bool BtrfsMgr::is_rw_volume(std::string vol_name) { struct stat buffer; std::string path = this->get_base_path(); path += "/" + vol_name + "/read_write"; return ((stat (path.c_str(), &buffer) == 0) && S_ISREG(buffer.st_mode)); } #define COMMENT_LINE "#" #define PARAM_DELIM ":=:" #define CONFIG_FILE_PATH "/config" #define LOG_LEVEL "log_level" #define CONTAINER_ATTR_MAX_COUNT "container_attr_max_count" #define OBJECT_ATTR_MAX_COUNT "object_attr_max_count" #define SNAP_LIST_MAX_COUNT "snap_list_max_count" #define SNAP_ATTR_MAX_COUNT "snap_attr_max_count" #define MAX_DATA_THREADS "max_data_threads" #define MAX_PULL_RANGES "max_pull_ranges" #define RANGE_SIZE "range_size" #define DATA_BUF_SIZE "data_buf_size" #define BUFS_IN_TRANSIT "bufs_in_transit" #define API_INPUT_FILE "api_input_file" #define DEFAULT_INUM_AND_GEN "default_inum_and_gen" #define MAX_PUSH_ANON_RANGES "max_push_anon_ranges" #define MAX_PUSH_HOLE_SIZE "max_push_hole_size" #define MAX_PUSH_DATA_SIZE "max_push_data_size" #define NON_SAN_SYSTEM "non_san_system" #define MAX_SEND_MSGS "max_send_msgs" #define MAX_RCV_MSGS "max_rcv_msgs" #define BTRFS_LOG_FILE "log_file" #define DEBUG_MEM_POOL "debug_mem_pool" #define SMALOG_FILE "/tmp/SmaBtrfs.log" #define NW_STACK "nw_stack" #define NW2_WORKER_CNT "nw2_worker_cnt" #define NW2_POLL_TIMEOUT "nw2_poll_timeout" #define NW2_MAX_READ "nw2_max_read_size" #define NW2_MAX_WRITE "nw2_max_write_pkts" #define MAX_CONNS "max_conns" #define VOL_OBJ_MAX_COUNT "vol_obj_max_count" bool BtrfsMgr::config_init() { std::string path = this->get_base_path(); std::ifstream config_file; path += CONFIG_FILE_PATH; config_file.open(path.c_str(), std::ios::in | std::ios::binary); logging_file_ = SMALOG_FILE; bzero(&btrfs_config_, sizeof(btrfs_config_)); btrfs_config_.log_level_ = SMALOG_INFO; btrfs_config_.snap_list_max_count_ = MAX_NO_SNAPS; btrfs_config_.buf_size_ = SMA_DEFAULT_BUF_SIZE; btrfs_config_.range_size_ = SMA_DEFAULT_RANGE_SIZE; if (! config_file.good()) { return true; } std::string line; while (std::getline(config_file, line)) { // Skip over empty lines and lines beginning with '#'. if (line.size() == 0 || line.compare(0, strlen(COMMENT_LINE), COMMENT_LINE) == 0) { continue; } size_t pos = 0; uint32_t config_val = 0; if ((pos = line.find(PARAM_DELIM)) != std::string::npos) { std::string key = line.substr(0, pos); std::string val = line.substr(pos + strlen(PARAM_DELIM)); if (key.compare(LOG_LEVEL) == 0) { std::istringstream convert(val); convert >> btrfs_config_.log_level_; if (btrfs_config_.log_level_ == 0) { std::cerr << "config invalid log_level" << std::endl; return false; } continue; } if (key.compare(CONTAINER_ATTR_MAX_COUNT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.container_attr_max_count_; if (btrfs_config_.container_attr_max_count_ == 0) { std::cerr << "config invalid container attr" << std::endl; return false; } continue; } if (key.compare(OBJECT_ATTR_MAX_COUNT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.object_attr_max_count_; if (btrfs_config_.object_attr_max_count_ == 0) { std::cerr << "config invalid object attr" << std::endl; return false; } continue; } if (key.compare(SNAP_LIST_MAX_COUNT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.snap_list_max_count_; if (btrfs_config_.snap_list_max_count_ == 0) { std::cerr << "config invalid snap list max" << std::endl; return false; } continue; } if (key.compare(SNAP_ATTR_MAX_COUNT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.snap_attr_max_count_; if (btrfs_config_.snap_attr_max_count_ == 0) { std::cerr << "config invalid snap attr max" << std::endl; return false; } continue; } if (key.compare(MAX_DATA_THREADS) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_data_threads_; if (btrfs_config_.max_data_threads_ == 0) { std::cerr << "config invalid max threads" << std::endl; return false; } continue; } if (key.compare(MAX_PULL_RANGES) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_pull_ranges_; if (btrfs_config_.max_pull_ranges_ == 0) { std::cerr << "config invalid max ranges" << std::endl; return false; } continue; } if (key.compare(RANGE_SIZE) == 0) { std::istringstream convert(val); convert >> btrfs_config_.range_size_; if (btrfs_config_.range_size_ == 0) { std::cerr << "config invalid range size" << std::endl; return false; } continue; } if (key.compare(DATA_BUF_SIZE) == 0) { std::istringstream convert(val); convert >> btrfs_config_.buf_size_; if (btrfs_config_.buf_size_ == 0) { std::cerr << "config invalid buf size" << std::endl; return false; } continue; } if (key.compare(BUFS_IN_TRANSIT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.bufs_in_transit_; if (btrfs_config_.bufs_in_transit_ == 0) { std::cerr << "config invalid bufs transit" << std::endl; return false; } continue; } if (key.compare(API_INPUT_FILE) == 0) { btrfs_config_.api_input_file_ = new char [val.length() + 1](); memcpy(btrfs_config_.api_input_file_, (char *) val.c_str(), val.length()); continue; } if (key.compare(DEFAULT_INUM_AND_GEN) == 0) { if (val.compare("true") == 0) { btrfs_config_.default_inum_and_gen_ = true; } else if (val.compare("false")) { std::cerr << "config invalid default inum and gen" << std::endl; return false; } continue; } if (key.compare(MAX_PUSH_ANON_RANGES) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_push_anon_ranges_; if (btrfs_config_.max_push_anon_ranges_ == 0) { std::cerr << "config invalid max push ranges" << std::endl; return false; } continue; } if (key.compare(MAX_PUSH_HOLE_SIZE) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_push_hole_size_; if (btrfs_config_.max_push_hole_size_ == 0) { std::cerr << "config invalid max push hole" << std::endl; return false; } continue; } if (key.compare(MAX_PUSH_DATA_SIZE) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_push_data_size_; if (btrfs_config_.max_push_data_size_ == 0) { std::cerr << "config invalid max push data" << std::endl; return false; } continue; } if (key.compare(NON_SAN_SYSTEM) == 0) { if (val.compare("true") == 0) { btrfs_config_.non_san_system_ = true; } else if (val.compare("false")) { std::cerr << "config invalid non san system" << std::endl; return false; } continue; } if (key.compare(MAX_SEND_MSGS) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_send_msgs_; if (btrfs_config_.max_send_msgs_ == 0) { std::cerr << "config invalid max send" << std::endl; return false; } continue; } if (key.compare(MAX_RCV_MSGS) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_rcv_msgs_; if (btrfs_config_.max_rcv_msgs_ == 0) { std::cerr << "config invalid max rcv" << std::endl; return false; } continue; } if (key.compare(BTRFS_LOG_FILE) == 0) { logging_file_ = val; continue; } if (key.compare(DEBUG_MEM_POOL) == 0) { if (val.compare("true") == 0) { btrfs_config_.debug_mem_pool_ = true; } else { btrfs_config_.debug_mem_pool_ = false; } continue; } if (key.compare(NW_STACK) == 0) { std::istringstream convert(val); convert >> btrfs_config_.network_stack_; if (btrfs_config_.network_stack_ <= 0) { std::cerr << "config invalid network_stack" << std::endl; return false; } continue; } if (key.compare(NW2_WORKER_CNT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.nw2_worker_cnt_; if (btrfs_config_.nw2_worker_cnt_ <= 0) { std::cerr << "config invalid nw2_worker_cnt" << std::endl; return false; } continue; } if (key.compare(NW2_POLL_TIMEOUT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.nw2_poll_timeout_; continue; } if (key.compare(NW2_MAX_READ) == 0) { std::istringstream convert(val); convert >> btrfs_config_.nw2_max_read_size_; if (btrfs_config_.nw2_max_read_size_ <= 0) { std::cerr << "config invalid nw2_max_read_size" << std::endl; return false; } continue; } if (key.compare(NW2_MAX_WRITE) == 0) { std::istringstream convert(val); convert >> btrfs_config_.nw2_max_write_pkts_; if (btrfs_config_.nw2_max_write_pkts_ <= 0) { std::cerr << "config invalid nw2_max_write_pkts" << std::endl; return false; } continue; } if (key.compare(MAX_CONNS) == 0) { std::istringstream convert(val); convert >> btrfs_config_.max_conns_; if (btrfs_config_.max_conns_ <= 0) { std::cerr << "config invalid max_conns" << std::endl; return false; } continue; } if (key.compare(VOL_OBJ_MAX_COUNT) == 0) { std::istringstream convert(val); convert >> btrfs_config_.vol_obj_max_count_; if (btrfs_config_.vol_obj_max_count_ <= 0) { std::cerr << "config invalid vol_obj_max_count" << std::endl; return false; } continue; } std::cerr << "Unknown config parameter" << std::endl; return false; } } return true; } /** * BtrfsMgr::is_sf_emulation */ bool BtrfsMgr::is_sf_emulation() { return( ! btrfs_config_.non_san_system_); } // get the file size uint32_t BtrfsMgr::get_file_size(std::string file_path) { // check if file exists and find its size uint32_t fsize = 0; std::streampos begin,end; std::ifstream opq_file(file_path.c_str(), ios::binary); if (opq_file.is_open()) { begin = opq_file.tellg(); opq_file.seekg (0, ios::end); end = opq_file.tellg(); opq_file.close(); fsize = (end-begin); } return fsize; } // write to 'opaque' file SmaStatus BtrfsMgr::write_to_file(std::string dir_path, std::string file_name, char *buffer, uint32_t len) { SmaStatus ret = SMA_OK; std::string file_path = dir_path + "/" + file_name; std::ofstream out_file(file_path.c_str(), ios::out | ios::binary | ios::trunc); if (out_file.is_open ()) { out_file.write(buffer, len); out_file.close(); } else { BTRFS_ERR_PRINT("Failed to open file %s, errno %d\n", file_path.c_str(), errno); // return the error ret = SMA_NOT_FOUND; } return ret; } /** Function BtrfsMgr::remove_vol_name * Function to remove volume uuid to name mapping. * @param uuid - Volume uuid * @param vol_name - Volume name */ SmaStatus BtrfsMgr::remove_vol_name(uuid_t *uuid, const std::string &vol_name) { SmaStatus ret = SMA_OK; std::string file_path = vol_uuid_path_; std::ifstream in_file; char* uuid_str = NULL; uint32_t st = 0; uuid_to_string(uuid, &uuid_str, &st); // allocates mem for uuid_str. if (uuid_str == NULL || st != uuid_s_ok) { BTRFS_UUID_PRINT(uuid, "Failed to convert UUID to string. " "Volume id: %s.\n", vol_name.c_str()); ret = SMA_NOMEM; goto out; } // Remove the file with path /btrfs/mtdt_vol/VOL_UUID/ file_path += "/"; file_path += uuid_str; in_file.open(file_path.c_str()); if (in_file) { if (remove(file_path.c_str()) == 0) { BTRFS_PRINT("opaque file %s deleted.\n", file_path.c_str()); ret = SMA_OK; } } in_file.close(); out: if (st == uuid_s_ok) { free(uuid_str); } return ret; } /** Function BtrfsMgr::store_vol_name * Function to store volume uuid to name mapping. * @param uuid - Volume uuid * @param vol_name - Volume name */ SmaStatus BtrfsMgr::store_vol_name(uuid_t *uuid, const std::string &vol_name) { SmaStatus ret = SMA_OK; std::string dir_path = vol_uuid_path_; char* uuid_str = NULL; uint32_t st = 0; char *buffer = NULL; uuid_to_string(uuid, &uuid_str, &st); // allocates mem for uuid_str. if (uuid_str == NULL || st != uuid_s_ok) { BTRFS_UUID_PRINT(uuid, "Failed to convert UUID to string. " "Volume id: %s.\n", vol_name.c_str()); ret = SMA_NOMEM; goto out; } // the content will be the vol_name buffer = new char[vol_name.length() + 1]; if (buffer == NULL) { BTRFS_ERR_PRINT("Failed to allocate mem for vol_id: %s\n", vol_name.c_str()); ret = SMA_NOMEM; goto out; } // populate the buffer strcpy(buffer, vol_name.c_str()); BTRFS_PRINT("Store the value %s for vol_uuid: %s\n", buffer, uuid_str); // Save the content in path /btrfs/mtdt_vol/VOL_UUID/ ret = write_to_file(dir_path, uuid_str, buffer, vol_name.length() + 1); delete[] buffer; out: if (st == uuid_s_ok) { free(uuid_str); } return ret; } /** Function BtrfsMgr::get_vol_name * Function to fetch name for a given volume uuid * @param uuid - Volume uuid * @param vol_name - Volume name */ SmaStatus BtrfsMgr::get_vol_name(uuid_t *uuid, std::string &volume) { SmaStatus ret = SMA_NOT_FOUND; assert(uuid); std::string dir_path = vol_uuid_path_; char* uuid_str = NULL; uint32_t st = 0; char *buffer = NULL; uint32_t fsize = 0; std::string file_path; std::ifstream file; uuid_to_string(uuid, &uuid_str, &st); // allocates mem for uuid_str. if (uuid_str == NULL || st != uuid_s_ok) { BTRFS_UUID_PRINT(uuid, "Failed to convert UUID to string. " "Volume id: %llu.\n"); ret = SMA_NOMEM; goto out; } file_path = dir_path + "/" + uuid_str; // read the volume name in the file /btrfs/mtdt_vol/VOL_UUID/ file.open(file_path.c_str(), ios::in | ios::binary); if (!file.is_open()) { BTRFS_ERR_PRINT("File %s does not exist.\n", file_path.c_str()); goto out; } // check if file exists and find its size fsize = get_file_size(file_path); if (!fsize) { BTRFS_ERR_PRINT("File %s is empty.\n", file_path.c_str()); goto out; } // allocate memory for a buffer of appropriate dimension buffer = new char[fsize + 1](); if (buffer == NULL) { BTRFS_ERR_PRINT("Failed to allocate mem for vol_uuid: %s\n", uuid_str); ret = SMA_NOMEM; goto out; } file.read(buffer, fsize); // Read the file content into the buffer BTRFS_DEBUG_PRINT("Got the value %s for vol_uuid: %s\n", buffer, uuid_str); volume.assign(buffer); ret = SMA_OK; out: delete [] buffer; file.close(); if (st == uuid_s_ok) { free(uuid_str); } return ret; } SmaStatus BtrfsMgr::create_dir(const char *path) { SmaStatus ret = SMA_OK; mode_t mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; struct stat st; if (stat(path, &st) != 0) { // dir entry doesnt exist if (mkdir(path, mode) != 0 && errno != EEXIST) { BTRFS_ERR_PRINT("mkdir %s failed with error %d\n", path, errno); // return the error ret = SMA_NOT_FOUND; } } else if (!S_ISDIR(st.st_mode)) { BTRFS_ERR_PRINT("%s is not a DIR\n", path); errno = ENOTDIR; // return the error ret = SMA_NOT_FOUND; } return ret; } // store vol_info record SmaStatus BtrfsMgr::store_vol_info(void *vol_id, uint32_t type_no, SmaOpaqueVolumeInfo *vol_info) { SmaStatus ret = SMA_OK; assert(vol_id); std::string dir_path = get_mtdt_vol_path(vol_id); // create the /btrfs/mtdt_vol/vol_id dir entry if it doesnt exist ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // create the /btrfs/mtdt_vol/vol_id/VOL dir entry if it doesnt exist dir_path += "/"; dir_path += vol_dir_; ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // create the /btrfs/mtdt_vol/vol_id/VOL/type_no dir entry // if it doesnt exist std::string type_no_str; // stream used for the conversion std::ostringstream convert; convert << type_no; type_no_str = convert.str(); dir_path += "/" + type_no_str; ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // write the vol_info to the fs uint32_t buf_sz = sizeof(uint32_t) + vol_info->opaque_size_; char *buffer = new char[buf_sz](); // populate the buffer uint32_t off = 0; uint32_t len = sizeof(uint32_t); uint32_t total_len = len; memcpy(buffer+off, &vol_info->opaque_size_, len); off = len; len = vol_info->opaque_size_; total_len += len; memcpy(buffer+off, vol_info->opaque_, len); ret = write_to_file(dir_path, opaque_fn_, buffer, total_len); delete[] buffer; return ret; } // delete vol_info record SmaStatus BtrfsMgr::remove_vol_info(void *vol_id, uint32_t type_no, SmaOpaqueVolumeInfo *vol_info) { SmaStatus ret = SMA_NOT_FOUND; assert(vol_id); std::string dir_path = get_mtdt_vol_path(vol_id); // remove the file with the following path // /btrfs/mtdt_vol/vol_id/VOL/type_no/opaque_fn_ dir_path += "/"; dir_path += vol_dir_; std::string type_no_str; // stream used for the conversion std::ostringstream convert; convert << type_no; type_no_str = convert.str(); dir_path += "/" + type_no_str; std::string file_path = dir_path + "/" + opaque_fn_; std::ifstream in_file(file_path.c_str()); if (in_file) { if (remove(file_path.c_str()) == 0) { BTRFS_PRINT("opaque file %s deleted\n", file_path.c_str()); ret = SMA_OK; } } return ret; } // read vol_info record SmaStatus BtrfsMgr::read_vol_info(void *vol_id, uint32_t type_no, SmaOpaqueVolumeInfo *vol_info) { SmaStatus ret = SMA_NOT_FOUND; assert(vol_id); std::string dir_path = get_mtdt_vol_path(vol_id); // read the file with the following path // /btrfs/mtdt_vol/vol_id/VOL/type_no/opaque_fn_ dir_path += "/"; dir_path += vol_dir_; std::string type_no_str; // stream used for the conversion std::ostringstream convert; convert << type_no; type_no_str = convert.str(); dir_path += "/" + type_no_str; std::string file_path = dir_path + "/" + opaque_fn_; // check if file exists and find its size uint32_t fsize = get_file_size(file_path); if (!fsize) { // return the error return ret; } // lets read the data and populate vol_info std::ifstream in_file(file_path.c_str(), ios::in | ios::binary); if (in_file.is_open()) { in_file.read((char *)&vol_info->opaque_size_, sizeof(vol_info->opaque_size_)); // allocate the memory for opaque_ vol_info->opaque_ = new uint8_t[vol_info->opaque_size_](); // read the payload in_file.read((char *)vol_info->opaque_, vol_info->opaque_size_); in_file.close(); } // verify if expected data is same as file size uint32_t rd_size = sizeof(vol_info->opaque_size_) + vol_info->opaque_size_; assert(fsize == rd_size); ret = SMA_OK; return ret; } int open_subvol_dir(const char *fname, DIR **dirstream) { int ret; struct stat st; int fd; ret = stat(fname, &st); if (ret < 0) { return -1; } if (S_ISDIR(st.st_mode)) { *dirstream = opendir(fname); if (!*dirstream) return -1; fd = dirfd(*dirstream); } else { return -1; } if (fd < 0) { fd = -1; if (*dirstream) { closedir(*dirstream); *dirstream = NULL; } } return fd; } /* * Test if path is a directory * Returns: * 0 - path exists but it is not a directory * 1 - path exists and it is a directory * < 0 - error */ static int test_isdir(const char *path) { struct stat st; int ret; ret = stat(path, &st); if (ret < 0) { return -errno; } return !!S_ISDIR(st.st_mode); } static int btrfs_create_subvolume(const char *subvol_path) { int retval = 0, res; int fddst = -1; DIR *dirstream = NULL; #ifdef _BTRFS_DEVEL_ retval = 1; /* failure */ res = test_isdir(subvol_path); if (res < 0 && res != -ENOENT) { BTRFS_ERR_PRINT("cannot access %s: %s\n", subvol_path, strerror(-res)); return retval; } if (res >= 0) { BTRFS_DEBUG_PRINT("target path already exists: %s\n", subvol_path); retval = 0; return retval; } std::string newname(get_basename(subvol_path)); if (newname.length() > BTRFS_PATH_NAME_MAX) { BTRFS_ERR_PRINT("name too long: (%s)\n", newname.c_str()); return retval; } std::string dstdir(get_dirname(subvol_path)); fddst = open_subvol_dir(dstdir.c_str(), &dirstream); if (fddst < 0) { return retval; } BTRFS_PRINT("Create subvolume '%s/%s'\n", dstdir.c_str(), newname.c_str()); struct btrfs_ioctl_vol_args args; memset(&args, 0, sizeof(args)); strncpy(args.name, newname.c_str(), sizeof(args.name)); args.name[sizeof(args.name) - 1] = '\0'; res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); if (res < 0) { BTRFS_ERR_PRINT("cannot create subvolume: %s\n", strerror(errno)); } else { retval = 0; /* success */ } closedir(dirstream); #endif // _BTRFS_DEVEL_ return retval; } /* * Create snapshot for a given subvolume on btrfs. * 1. Creates snapshot on btrfs * 2. Creates UUID for snapshot. * 3. Returns the snap_id and SmaSnapCreateOutput populated. */ SmaStatus BtrfsMgr::create_snapshot(void *vol_id, //IN SmaSnapCreateInput *input, //IN SmaSnapCreateOutput *output, //OUT void *snap_id) //OUT { SmaStatus status = SMA_NOT_FOUND; struct stat snap_st = {0}; struct timespec crtime = {0}; int res = 0; uint32_t st = 0; #ifdef _BTRFS_DEVEL_ // Required parameters. assert(input->snap_name_); assert(snap_id); assert(vol_id); std::string subvol_src = get_full_path(vol_id); /* * Snap root is present at * /btrfs/snap_vol/vol_id */ std::string subvol_snap_root = get_snapvol_path(vol_id); std::string snapname(input->snap_name_); /* * Create the subvolume snap root if it doesnt exists * create the /btrfs/snap_vol/vol_id subvolume * * btrfs_create_subvolume returns success if the subvolume * already exists */ res = btrfs_create_subvolume(subvol_snap_root.c_str()); if (res) { return status; } /* * Snapshot is present at * /btrfs/snap_vol/vol_id/snapname */ std::string snap_dir_path = subvol_snap_root + "/" + snapname; DIR *dirstream1 = NULL; DIR *dirstream2 = NULL; int fd = open_subvol_dir(subvol_src.c_str(), &dirstream1); if (fd < 0) { BTRFS_ERR_PRINT("Failed to open src %s dir\n", subvol_src.c_str()); return status; } // Flush Cached data to the filesystem // for snapshot consistency. May not be needed. // syncfs(fd); int fddst = open_subvol_dir(subvol_snap_root.c_str(), &dirstream2); if (fddst < 0) { BTRFS_ERR_PRINT("Failed to open snap %s dir\n", subvol_snap_root.c_str()); closedir(dirstream1); return status; } /* * Verify that the snap doesn't exist. * Make sure no of snaps < btrfs_config_.snap_list_max_count_. */ uint64_t top_id = 0; std::vector snap_name_list; std::vector snap_id_list; if ((res = btrfs_list_get_path_rootid(fddst, &top_id)) != 0) { BTRFS_ERR_PRINT("can't get rootid for '%s'\n", subvol_snap_root.c_str()); status = SMA_NOT_FOUND; goto out; } if ((res = list_subvol_search(fd, top_id, snap_name_list, snap_id_list)) == 0 && snap_name_list.size() > 0) { if (snap_name_list.size() >= btrfs_config_.snap_list_max_count_ && is_sf_emulation()) { BTRFS_ERR_PRINT("Snapshot limit reached. No of snaps:%d\n", btrfs_config_.snap_list_max_count_); status = SMA_MAX_COUNT; goto out; } if (snap_name_list.end() != std::find(snap_name_list.begin(), snap_name_list.end(), snapname)) { BTRFS_ERR_PRINT("Snapshot already present: %s\n", snapname.c_str()); status = SMA_DUPLICATE_ENTRY; goto out; } } else if (res) { BTRFS_ERR_PRINT("Snaplist returned error for subvol: '%s'\n", subvol_snap_root.c_str()); status = SMA_NOT_FOUND; goto out; } struct btrfs_ioctl_vol_args_v2 args; memset(&args, 0, sizeof(args)); args.flags |= BTRFS_SUBVOL_RDONLY; BTRFS_PRINT("Create a readonly snapshot of '%s' in '%s/%s'\n", subvol_src.c_str(), subvol_snap_root.c_str(), snapname.c_str()); args.fd = fd; strncpy(args.name, snapname.c_str(), BTRFS_PATH_NAME_MAX - 1); args.name[snapname.length()] = '\0'; res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); if (res < 0) { BTRFS_ERR_PRINT("snapshot create failed '%s': %s\n", snapname.c_str(), strerror(errno)); goto out; } *(uint64_t *)snap_id = get_snapvol_id(vol_id, snapname); assert(*(uint64_t *)snap_id != 0); /* * Successfully created snapshot on Btrfs. Create an * instance uuid to assign. */ uuid_create(&output->instance_uuid_, &st); if (st != uuid_s_ok) { BTRFS_ERR_PRINT("Failed creating snap uuid for:%s Err:%u\n", input->snap_name_, st); status = SMA_INTERNAL_ERROR; goto out; } // Assign create time from the filesystem. bzero(&snap_st, sizeof(struct stat)); res = stat(snap_dir_path.c_str(), &snap_st); if (res < 0) { BTRFS_ERR_PRINT("Failed getting creation time for dir: %s\n", snap_dir_path.c_str()); status = SMA_NOT_FOUND; goto out; } crtime = snap_st.st_ctim; output->instance_create_time_ = crtime.tv_sec; status = SMA_OK; /* success */ out: if (status != SMA_OK) { SmaStatus _s = delete_snapshot(vol_id, snap_id); } if (dirstream1) closedir(dirstream1); if (dirstream2) closedir(dirstream2); #endif // _BTRFS_DEVEL_ return status; } /* * Stores the mapping of snapshot version_uuid to btrfs snapshot id. * The mapping in currently put into files. * /btrfs/mtdt_vol/vol_id/SNAP/type_no/version_uuid will have snap_id as content. */ SmaStatus BtrfsMgr::store_snap_id(void *vol_id, void *snap_id, uuid_t *snap_uuid) { SmaStatus ret = SMA_OK; uint32_t type_no = (uint32_t) SMA_VERSION_UUID_2_ID_OPAQUE_TYPE; assert(vol_id); assert(snap_id); std::string dir_path = get_mtdt_vol_path(vol_id); // create the /btrfs/mtdt_vol/vol_id dir entry if it doesnt exist ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // create the /btrfs/mtdt_vol/vol_id/SNAP dir entry if it doesnt exist dir_path += "/"; dir_path += snap_dir_; ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // create the /btrfs/mtdt_vol/vol_id/SNAP/type_no dir entry // if it doesnt exist std::string type_no_str; // stream used for the conversion std::ostringstream convert; convert << type_no; type_no_str = convert.str(); dir_path += "/" + type_no_str; ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // create /btrfs/mtdt_vol/vol_id/SNAP/type_no/version_uuid dir entry // the content will be the snap_id given. uint32_t buf_sz = sizeof(uint64_t); char *buffer = new char[buf_sz](); if (buffer == NULL) { BTRFS_ERR_PRINT("Failed to allocate mem for snap_id: %llu\n", *(uint64_t *)snap_id); return SMA_NOMEM; } // populate the buffer memcpy(buffer, (char *)snap_id, buf_sz); char* uuid_str = NULL; uint32_t st = 0; uuid_to_string(snap_uuid, &uuid_str, &st); // allocates mem for uuid_str. if (uuid_str == NULL || st != uuid_s_ok) { BTRFS_UUID_PRINT(snap_uuid, "Failed to convert UUID to string. Snapshot id: %llu.\n", *(uint64_t *)snap_id); ret = SMA_NOMEM; goto done; } BTRFS_PRINT("Writing to file. Snap UUID: %s Btrfs SnapID: %llu.\n", uuid_str, *(uint64_t *)snap_id); ret = write_to_file(dir_path, uuid_str, buffer, buf_sz); done: delete[] buffer; free(uuid_str); return ret; } /* * Given a snap_uuid, this function returns the Btrfs snap_id. */ SmaStatus BtrfsMgr::get_snap_id(void *vol_id, //IN uuid_t *snap_uuid, //IN void *snap_id) //OUT { // Default return value if snap not found. SmaStatus ret = SMA_NOT_FOUND; uint32_t type_no = (uint32_t) SMA_VERSION_UUID_2_ID_OPAQUE_TYPE; assert(vol_id); assert(snap_id); // /btrfs/mtdt_vol/vol_id std::string dir_path = get_mtdt_vol_path(vol_id); // /btrfs/mtdt_vol/vol_id/SNAP dir_path += "/"; dir_path += snap_dir_; // /btrfs/mtdt_vol/vol_id/SNAP/type_no std::string type_no_str; std::ostringstream convert; convert << type_no; type_no_str = convert.str(); dir_path += "/" + type_no_str; char* uuid_str = NULL; uint32_t st = 0; uuid_to_string(snap_uuid, &uuid_str, &st); // allocates mem for uuid_str. if (uuid_str == NULL || st != uuid_s_ok) { BTRFS_UUID_PRINT(snap_uuid, "Failed to convert UUID to string. Snapshot id: %llu.\n", *(uint64_t *)snap_id); return SMA_NOMEM; } // make sure file is present. std::string name(uuid_str); std::string fpath = dir_path + "/" + name; struct stat fst; if (stat(fpath.c_str(), &fst) < 0 || !S_ISREG(fst.st_mode)) { BTRFS_ERR_PRINT("Did not find snap id for snap uuid: %s", uuid_str); return ret; } std::ifstream in_file(fpath.c_str(), ios::in | ios::binary); in_file.read((char *)snap_id, sizeof(uint64_t)); in_file.close(); BTRFS_DEBUG_PRINT("Found snapshot: %s BtrfsID:%llu\n", name.c_str(), *(uint64_t *)snap_id); ret = SMA_OK; free(uuid_str); return ret; } /* * BtrfsMgr::store_snap_info * store snap_info record of the user created snapshot. * @param vol_name - Volume name * @param snap_id - Btrfs Snapshot id * @param snap_name - Snapshot name for which information is not there * @param version_info - SmaSnapshotInfo struct for version_info * @param instance_info - SmaSnapshotInfo struct for instance info * @return SmaStatus */ SmaStatus BtrfsMgr::store_snap_info(std::string vol_name, uint64_t snap_id, std::string snap_name, SmaSnapshotInfo *version_info, SmaSnapshotInfo *instance_info) { MtdtStore *mtds = get_mtdt_store(); SmaStatus status = SMA_OK; /* Store version information on Btrfs.*/ { SnapOpaqueEntry snap_info(sizeof(SmaSnapshotInfo), (uint8_t *) version_info); OpaqueReq req((void*)vol_name.c_str(), (void*)&snap_id, NULL, SNAP, SNAP_VERSION_INFO, 1, &snap_info); if (mtds->store_opaque(&req) != 0) { BTRFS_UUID_PRINT(&version_info->uuid_, "Failed to store version info " "for snapshot %s.\n", vol_name.c_str()); status = SMA_INTERNAL_ERROR; goto out; } } { SnapOpaqueEntry snap_info(sizeof(SmaSnapshotInfo), (uint8_t *) instance_info); OpaqueReq req((void*)vol_name.c_str(), (void*)&snap_id, NULL, SNAP, SNAP_INSTANCE_INFO, 1, &snap_info); if (mtds->store_opaque(&req) != 0) { BTRFS_UUID_PRINT(&instance_info->uuid_, "Failed to store instance info " "for snapshot %s.\n", snap_name.c_str()); status = SMA_INTERNAL_ERROR; goto out; } } { OpaqueReq req((void*)vol_name.c_str(), (void*)&snap_id, &version_info->uuid_, UUID_2_ID, SNAP_UUID_2_ID, 0, NULL); /* Store version_uuid to snap_id mapping.*/ if (mtds->store_opaque(&req) != 0) { BTRFS_UUID_PRINT(&version_info->uuid_, "Failed to store snap id mapping for " "snapshot %s Snap ID: %llu.\n", snap_name.c_str(), snap_id); status = SMA_INTERNAL_ERROR; goto out; } } BTRFS_UUID_PRINT(&(version_info->uuid_), "Created snapshot information. Name:%s Create " "Time:%llu\n", snap_name.c_str(), version_info->create_time_); out: if (status != SMA_OK) { // Delete all the opaque information about the snapshot. OpaqueReq req((void*)vol_name.c_str(), (void*)&snap_id, NULL, SNAP, SNAP_VERSION_INFO, 1, NULL); mtds->remove_opaque(&req); req.type_no_ = SNAP_INSTANCE_INFO; mtds->remove_opaque(&req); } return status; } #include /* * BtrfsMgr::fill_user_snap_info * For a user created snapshot, stamp the snapshot information(version uuid, * instance uuid and uuid to id). * @param vol_name - Volume name * @param snap_id - Btrfs Snapshot id * @param snap_name - Snapshot name for which information is not there * @param snap - SmaSnapshotInfo output * @return SmaStatus */ SmaStatus BtrfsMgr::fill_user_snap_info(std::string vol_name, uint64_t snap_id, std::string snap_name, SmaSnapshotInfo **snap) { SmaStatus status = SMA_NOT_FOUND; struct stat snap_st = {0}; struct timespec crtime = {0}; int res = 0; uint32_t st = 0; std::string subvol_snap_root = get_snapvol_path((void*)vol_name.c_str()); std::string snap_dir_path = subvol_snap_root + "/" + snap_name; *snap = new SmaSnapshotInfo(); if (!*snap) { BTRFS_ERR_PRINT("Failed to allocate memory for SmaSnapshotInfo" " object: %s\n", vol_name.c_str()); status = SMA_NOMEM; goto out; } uuid_create(&((*snap)->uuid_), &st); if (st != uuid_s_ok) { BTRFS_ERR_PRINT("Failed creating snap uuid for:%s Err:%u\n", snap_name.c_str(), st); status = SMA_INTERNAL_ERROR; goto out; } // Snapshot is present at /btrfs/snap_vol/vol_id/snapname res = stat(snap_dir_path.c_str(), &snap_st); if (res < 0) { BTRFS_ERR_PRINT("Failed getting creation time for dir: %s\n", snap_dir_path.c_str()); status = SMA_NOT_FOUND; goto out; } crtime = snap_st.st_ctim; (*snap)->create_time_ = crtime.tv_sec; status = store_snap_info(vol_name, snap_id, snap_name, *snap, *snap); if (status != SMA_OK) { BTRFS_ERR_PRINT("Failed storing user snapshot " "information for %s Err:%u\n", snap_name.c_str(), status); status = SMA_INTERNAL_ERROR; goto out; } out: return status; } bool BtrfsMgr::snap_id_to_uuid(std::string vol_name, uint64_t snap_id, uuid_t &version_uuid) { MtdtStore *mtds = get_mtdt_store(); OpaqueReq snap_req((void *) vol_name.c_str(), (void *) &snap_id, NULL, SNAP, SNAP_VERSION_INFO, 1, NULL); OpaqueList *snap_opaque; if ((snap_opaque = mtds->get_opaque(&snap_req)) == NULL) { return false; } SmaSnapshotInfo *snap_info = (SmaSnapshotInfo *) snap_opaque->entries_.snaps_[0].opaque_; version_uuid = snap_info->uuid_; mtds->dealloc_opaque_list(snap_opaque); return true; } /* * BtrfsMgr::populate_snap_list * Returns the snapshot information based on SmaSnapInfoType input. * 1) SMA_GET_SNAPSHOT_INFO - Looks up for the snapshot information for the * snapshot for a given version uuid or name. * 2) SMA_GET_SNAPSHOT_LIST - fills up SmaSnapInfo structure for all the snapshots * for a particular volume. * 3) SMA_GET_SNAPSHOT_LATEST - Finds the latest snapshot information for a given * volume. * @param input - SmaSnapListInput structure * @param output - SmaSnapListOutput structure * @param snap_names - List of snapshot names for a given volume * @param snap_ids - List of btrfs snapshot ids for the volume * @vol_name - Volume name of the volume * @return Type SmaStatus */ SmaStatus BtrfsMgr::populate_snap_list(SmaSnapListInput *input, SmaSnapListOutput *output, std::vector snap_names, std::vector snap_ids, std::string vol_name) { SmaStatus status = SMA_OK; MtdtStore *mtds = get_mtdt_store(); int max_version_create_time = 0; int index = 0; uint64_t redirect_snap_id = 0; output->count_ = (input->type_ == SMA_GET_SNAPSHOT_LIST) ? snap_names.size() : 0; if (input->type_ == SMA_GET_SNAPSHOT_REDIRECT) { OpaqueReq snap_req((void*)vol_name.c_str(), NULL, NULL, VOL, SNAP_REDIRECT_ID, 1, NULL); OpaqueList* snap_resp = NULL; if ((snap_resp = mtds->get_opaque(&snap_req)) == NULL) { status = SMA_OK; return status; } memcpy(&redirect_snap_id, snap_resp->entries_.vol_[0].opaque_, sizeof(uint64_t)); mtds->dealloc_opaque_list(snap_resp); } assert(0 < snap_names.size()); if (input->type_ == SMA_GET_SNAPSHOT_LIST) { assert(snap_names.size() <= input->max_count_); } for (uint32_t id = 0; id < snap_names.size(); id++) { std::string snap_name = snap_names[id]; uint32_t size = snap_name.size() + 1; struct stat st; int index = (input->type_ == SMA_GET_SNAPSHOT_LIST) ? id : 0; SmaSnapInfo& info = output->entries_[index]; SmaSnapshotInfo *entry = NULL; SmaSnapshotInfo *inst_info = NULL; OpaqueList* snap_inst_resp = NULL; OpaqueList* snap_ver_resp = NULL; bool created_snap_info = false; bool end_loop = false; OpaqueReq snap_req((void*)vol_name.c_str(), (void*)(&snap_ids[id]), NULL, SNAP, SNAP_VERSION_INFO, 1, NULL); if ((snap_ver_resp = mtds->get_opaque(&snap_req)) == NULL) { // This snapshot is a user created new snapshot // Fill the snapshot information since its not // present. // Note:This is btrfs specific implementation. // Ideally the platform should fill the entries // when snapshot is created. status = fill_user_snap_info(vol_name, snap_ids[id], snap_name, &entry); if (status != SMA_OK) { delete entry; goto out; } created_snap_info = true; } else { entry = (SmaSnapshotInfo*)( snap_ver_resp->entries_.snaps_[0].opaque_); } // Getting version info switch(input->type_) { case SMA_GET_SNAPSHOT_REDIRECT: { // Continue till you don't find the redirected snap id. if (redirect_snap_id != snap_ids[id]) { continue; } output->count_ = 1; end_loop = true; } break; case SMA_GET_SNAPSHOT_INFO: { // Compare the current snapshot name // with that of the requested snapshot // till we find the valid snapshot if (snap_name.compare(input->name_)) { goto cleanup; } output->count_ = 1; end_loop = true; } break; case SMA_GET_SNAPSHOT_LATEST: { // First snapshot in snap_name_list is latest snapshot max_version_create_time = entry->create_time_; output->count_ = 1; end_loop = true; } break; case SMA_GET_SNAPSHOT_LIST: break; default: assert(false); } // Getting instance info strcpy(info.name_, snap_name.c_str()); memcpy(&(info.version_uuid_), &(entry->uuid_), sizeof(uuid_t)); info.version_create_time_ = entry->create_time_; snap_req.type_no_ = SNAP_INSTANCE_INFO; if ((snap_inst_resp = mtds->get_opaque(&snap_req)) == NULL) { BTRFS_ERR_PRINT("Failed to find snapshot :%s", snap_name.c_str()); end_loop = true; status = SMA_INTERNAL_ERROR; goto cleanup; } inst_info = (SmaSnapshotInfo*)( snap_inst_resp->entries_.snaps_[0].opaque_); memcpy(&(info.instance_uuid_), &(inst_info->uuid_), sizeof(uuid_t)); info.instance_create_time_ = inst_info->create_time_; cleanup: if (created_snap_info) { delete entry; } else { mtds->dealloc_opaque_list(snap_ver_resp); } if (snap_inst_resp) { mtds->dealloc_opaque_list(snap_inst_resp); } if (end_loop) { break; } } out: assert(output->count_ <= input->max_count_); return status; } /* * BtrfsMgr::get_snap_list * Gets all the snapshot names and corresponding btrfs snapshot ids. * @vol_id - Volume id of the volume * @param snap_name_list - List of snapshot names for a particular volume id * @param snap_name_ids - List of btrfs snapshot ids for a particular volume id * @Return Type SmaStatus */ SmaStatus BtrfsMgr::get_snap_list(void *vol_id, std::vector &snap_name_list, std::vector &snap_id_list) { SmaStatus status = SMA_OK; #ifdef _BTRFS_DEVEL_ std::string subvol_snap_root = get_snapvol_path(vol_id); DIR *dirstream1 = NULL; int fd = open_subvol_dir(subvol_snap_root.c_str(), &dirstream1); if (fd < 0) { BTRFS_PRINT("Found NO snapshots for vol_id: %s\n", (char *)vol_id); return status; } uint64_t top_id = 0; int ret = btrfs_list_get_path_rootid(fd, &top_id); if (ret) { BTRFS_ERR_PRINT("can't get rootid for '%s'\n", subvol_snap_root.c_str()); status = SMA_NOT_FOUND; goto out; } // Iterate over the elements in the directory and populate // snap_name_list and snap_id_list ret = list_subvol_search(fd, top_id, snap_name_list, snap_id_list); if (ret || (!snap_name_list.size())) { BTRFS_PRINT("Found NO snapshots for vol_id: %s\n", (char *)vol_id); goto out; } std::reverse(snap_name_list.begin(), snap_name_list.end()); std::reverse(snap_id_list.begin(), snap_id_list.end()); out: closedir(dirstream1); #endif // _BTRFS_DEVEL_ return status; } // Check if volume with the volume id exist bool BtrfsMgr::check_volume_existance(const std::string& req_vol_id, std::string& vol_name) { bool status = false; #ifdef _BTRFS_DEVEL_ DIR *dirstream1 = NULL; std::vector vol_name_list; std::vector vol_id_list; uint64_t top_id = 0; int ret = 0; int fd = open_subvol_dir(this->get_base_path().c_str(), &dirstream1); if (fd < 0) { BTRFS_ERR_PRINT("Btrfs is not installed.\n"); goto out; } ret = btrfs_list_get_path_rootid(fd, &top_id); if (ret) { BTRFS_ERR_PRINT("can't get rootid for '%s'\n", this->get_base_path().c_str()); goto out; } ret = list_subvol_search(fd, top_id, vol_name_list, vol_id_list); if (ret || (!vol_name_list.size())) { BTRFS_ERR_PRINT("lookup failed for the root volume /btrfs"); goto out; } // search the given id from all the volumes in /btrfs for (uint32_t i = 0; i < vol_id_list.size(); i++) { std::string cur_name = vol_name_list[i]; uint64_t btrfs_vol_id = vol_id_list[i]; if (req_vol_id == btrfs_vol_id) { // we found the requested volume vol_name = cur_name; status = true; break; } } out: closedir(dirstream1); #endif return status; } // get snapshot subvolume id uint64_t BtrfsMgr::get_snapvol_id(void *vol_id, std::string snap_name) { uint64_t top_id = 0; #ifdef _BTRFS_DEVEL_ std::string snapvol_root = get_snapvol_path(vol_id); snapvol_root += "/" + snap_name; DIR *dirstream1 = NULL; int fd = open_subvol_dir(snapvol_root.c_str(), &dirstream1); if (fd < 0) { BTRFS_PRINT("snapshot subvolume %s, not found\n", snapvol_root.c_str()); return top_id; } int ret = btrfs_list_get_path_rootid(fd, &top_id); if (ret) { BTRFS_ERR_PRINT("can't get rootid for '%s'", snapvol_root.c_str()); top_id = 0; } closedir(dirstream1); #endif // _BTRFS_DEVEL_ return top_id; } // get the snapshot subvolume name for a given vol_id, snap_id std::string BtrfsMgr::get_snapvol_name(void *vol_id, void *snap_id) { std::string snapname; #ifdef _BTRFS_DEVEL_ // if snap_id is NULL then return the AFS path assert(vol_id); if (!snap_id) { snapname = get_full_path(vol_id); return snapname; } std::string subvol_snap_root = get_snapvol_path(vol_id); DIR *dirstream1 = NULL; int fd = open_subvol_dir(subvol_snap_root.c_str(), &dirstream1); if (fd < 0) { BTRFS_ERR_PRINT("Failed to open vol_id: %s\n", (char *)vol_id); return snapname; } uint64_t top_id = 0; std::vector snap_name_list; std::vector snap_id_list; int ret = btrfs_list_get_path_rootid(fd, &top_id); if (ret) { BTRFS_ERR_PRINT("can't get rootid for '%s'\n", subvol_snap_root.c_str()); goto out; } ret = list_subvol_search(fd, top_id, snap_name_list, snap_id_list); if (ret || (!snap_name_list.size())) { BTRFS_ERR_PRINT("lookup failed for snap_id %llu, vol_id: %s\n", (*(uint64_t *)snap_id), (char *)vol_id); goto out; } // search the given snap_id from all the snap_ids of the vol_id for (uint32_t i = 0; i < snap_id_list.size(); i++) { uint64_t req_snap_id = (*(uint64_t *)snap_id); std::string snap_name = snap_name_list[i]; uint64_t btrfs_snap_id = snap_id_list[i]; if (req_snap_id == btrfs_snap_id) { // we found the requested snap_id snapname = subvol_snap_root + "/" + snap_name; break; } } out: closedir(dirstream1); #endif return snapname; } #ifdef _BTRFS_DEVEL_ static int wait_for_commit(int fd) { int ret; ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL); if (ret < 0) return ret; return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL); } #endif // delete snapshot for given subvolume SmaStatus BtrfsMgr::delete_snapshot(void *vol_id, void *snap_id) { SmaStatus status = SMA_NOT_FOUND; #ifdef _BTRFS_DEVEL_ std::string ss_path = get_snapvol_name(vol_id, snap_id); if (ss_path.length() == 0) { BTRFS_ERR_PRINT("Failed to find snapshot for deletion: volume id (%s), snap id (%d)\n", vol_id, *(uint64_t *)snap_id); return SMA_SNAPSHOT_NOT_FOUND; } std::string ss_root_path(get_dirname(ss_path)); DIR *ss_root_dir_stream = NULL; int ss_fd = open_subvol_dir(ss_root_path.c_str(), &ss_root_dir_stream); if (ss_fd < 0) { BTRFS_ERR_PRINT("Failed to open fd (%d), snap root dir %s\n", ss_fd, ss_root_path.c_str()); return SMA_INTERNAL_ERROR; } struct btrfs_ioctl_vol_args args; memset(&args, 0, sizeof(args)); std::string ss_name(get_basename(ss_path)); if (ss_name.length() > BTRFS_PATH_NAME_MAX) { BTRFS_ERR_PRINT("Snapshot name too long: (%s)\n", ss_name.c_str()); return SMA_INTERNAL_ERROR; } BTRFS_PRINT("Delete readonly snapshot '%s' from '%s'\n", ss_name.c_str(), ss_root_path.c_str()); strcpy(args.name, ss_name.c_str()); int res = ioctl(ss_fd, BTRFS_IOC_SNAP_DESTROY, &args); if(res < 0 ){ BTRFS_ERR_PRINT("snapshot delete failed '%s': %s\n", ss_path.c_str(), strerror(errno)); status = SMA_SNAPSHOT_DELETE_FAILED; } else { res = wait_for_commit(ss_fd); if (res < 0) { BTRFS_ERR_PRINT("unable to wait for commit after snapshot delete for '%s': %s", ss_path.c_str(), strerror(errno)); status = SMA_INTERNAL_ERROR; } else { status = SMA_OK; } } closedir(ss_root_dir_stream); #endif // _BTRFS_DEVEL_ return status; } SmaStatus BtrfsMgr::rename_snapshot(void *vol_id, void *snap_id, char *new_name) { SmaStatus status = SMA_OK; std::string ss_path = get_snapvol_name(vol_id, snap_id); if (ss_path.length() == 0) { BTRFS_ERR_PRINT("Failed to find snapshot to rename: volume id (%s), snap id (%d)\n", vol_id, *(uint64_t *)snap_id); return SMA_SNAPSHOT_NOT_FOUND; } std::string new_path(get_dirname(ss_path) + "/" + new_name); BTRFS_PRINT("Renaming volume id (%s), snap id (%d), snapshot path (%s), new path (%s)\n", vol_id, *(uint64_t *)snap_id, ss_path.c_str(), new_path.c_str()); int rc = rename(ss_path.c_str(), new_path.c_str()); if (rc) { BTRFS_ERR_PRINT("Failed to rename snapshot (%s) to (%s), Error (%d)\n", ss_path.c_str(), new_path.c_str(), rc); status = SMA_SNAPSHOT_RENAME_FAILED; } else { BTRFS_PRINT("Renamed snapshot path (%s) to (%s)\n", ss_path.c_str(), new_path.c_str()); } return status; } SmaStatus BtrfsMgr::get_vol_location(std::string vol_id, std::string &vol_addr) { SmaStatus status = SMA_OK; std::ifstream conf; std::string confEntry; // On a VADP source endpoint, return local ip address if (getCurrentPlatformType(vol_id) == SMA_PLATFORM_TYPE_VADP) { if (getLocalIpAddr(vol_addr) == 0) { BTRFS_PRINT("Vol %s on local machine. Ip:%s\n", vol_id.c_str(), vol_addr.c_str()); } else { BTRFS_ERR_PRINT("Vol %s on local machine. Couldn't lookup Ip.\n", vol_id.c_str()); status = SMA_NOT_FOUND; } return status; } std::string filepath = btrfs_mount_path_ + "/" + VOL_LOC_CONF_FILE; conf.open(filepath.c_str()); if (!conf.is_open()) { BTRFS_PRINT("Volume location config file %s doesn't exist.\n", filepath.c_str()); // Check if volume exists on the local filesystem. // Currently volume name is the btrfs subvol id so lookup based on it std::string vol_name; // for satisfying the API. if (check_volume_existance(vol_id, vol_name)) { // Volume is present on same node. So indicate to reuse // the current IP for further ops. if (getLocalIpAddr(vol_addr) == 0) { BTRFS_PRINT("Vol %s on local machine. Ip:%s\n", vol_id.c_str(), vol_addr.c_str()); } else { BTRFS_ERR_PRINT("Vol %s on local machine. Couldn't lookup Ip.\n", vol_id.c_str()); status = SMA_NOT_FOUND; } } else { status = SMA_NOT_FOUND; } goto end; } while (std::getline(conf, confEntry)) { std::string entVName; std::string entVAddr; std::istringstream line(confEntry); if (!(line >> entVName >> entVAddr)) { status = SMA_CONFIG_ERR; BTRFS_ERR_PRINT("Volume location config file invalid format.\n"); goto end; } if (entVName.compare(vol_id) != 0) { continue; } else { vol_addr.clear(); vol_addr = entVAddr; BTRFS_PRINT("Volume location entry found. Vol:%s Addr:%s\n", entVName.c_str(), entVAddr.c_str()); goto end; } } status = SMA_NOT_FOUND; BTRFS_ERR_PRINT("Vol:%s location entry not found.\n", vol_id.c_str()); end: if (conf.is_open()) { conf.close(); } return status; } SmaStatus BtrfsMgr::get_diffs(const char *base, const char *incr, uint64_t starting_offset_, uint64_t length_, uint32_t max_count, bool isBaseline, SmaGetObjectRangesOutput *output) { bool hole = false, diff = false, found = false; uint64_t offset, ending_offset, index = 0; uint32_t cur_length; SmaStatus sma_status = SMA_OK; bzero(output->entries_, sizeof(SmaObjectRanges) * max_count); output->count_ = 0; offset = starting_offset_; ending_offset = starting_offset_ + length_; while (offset < ending_offset) { if ((ending_offset - offset) < btrfs_config_.buf_size_) { cur_length = ending_offset - offset; } else { cur_length = btrfs_config_.buf_size_; } if (isBaseline == false) { diff = false; hole = false; sma_status = isDifferent(base, incr, offset, cur_length, hole, diff); if (sma_status != SMA_OK) { BTRFS_ERR_PRINT("Hit error while generating " "diffs between files: %s and " " %s at offset %lu\n", base, incr, offset); goto out; } } else { sma_status = isHoleBaseline(incr, offset, cur_length, hole); if (sma_status != SMA_OK) { BTRFS_ERR_PRINT("Hit error while reading " "file: %s at offset %lu\n", incr, offset); goto out; } } if (diff == true || isBaseline == true) { if (output->entries_[index].length_) { if ((output->entries_[index].offset_ + output->entries_[index].length_) == offset && output->entries_[index].is_hole_ == hole) { output->entries_[index].length_ += cur_length; offset += cur_length; continue; } index++; } if (index >= max_count) { BTRFS_ERR_PRINT("Range count(%u) exceeded " "max_count (%u)\n", index+1, max_count); sma_status = SMA_OVERFLOW; goto out; } output->entries_[index].offset_ = offset; output->entries_[index].length_ = cur_length; output->entries_[index].is_hole_ = hole; found = true; } offset += cur_length; } out: if (sma_status == SMA_OK) { if (found) { output->count_ = index + 1; } } return sma_status; } SmaStatus BtrfsMgr::get_data(const char *incr, uint64_t starting_offset_, uint64_t cur_length_, SmaGetObjectDataOutput *output) { uint64_t offset = starting_offset_; SmaStatus sma_status = SMA_OK; std::ifstream in_file_incr(incr, ios::in | ios::binary); if (in_file_incr.is_open()) { in_file_incr.seekg(starting_offset_); in_file_incr.read((char *)output->buf_, cur_length_); if (!in_file_incr.good()) { goto error_out; } in_file_incr.close(); goto out; } error_out: BTRFS_ERR_PRINT("Failed to access file :%s\n", incr); sma_status = SMA_IO_ERROR; out: return sma_status; } SmaStatus BtrfsMgr::write_data(const char *file, SmaUpdateObjectDataInput *input, char *hole_buf) { SmaStatus sma_status = SMA_OK; std::ofstream out_file(file, ios::in | ios::out); if (! out_file.is_open()) { BTRFS_ERR_PRINT("Failed to open file :%s\n", file); goto error_out; } for (int i = 0; i < input->count_; i++) { out_file.seekp(input->entries_[i].starting_offset_, ios::beg); assert(input->entries_[i].length_ && input->entries_[i].length_ <= btrfs_config_.range_size_); if (input->entries_[i].is_hole_) { out_file.write((char *)hole_buf, input->entries_[i].length_); } else { out_file.write((char *)input->entries_[i].buf_, input->entries_[i].length_); } if (!out_file.good()) { BTRFS_ERR_PRINT("Failed to write at offset %lld and length %lld file :%s\n", input->entries_[i].starting_offset_, input->entries_[i].length_, file); goto error_out; } } out_file.close(); goto out; error_out: BTRFS_ERR_PRINT("Failed to access or write to file :%s\n", file); sma_status = SMA_IO_ERROR; out: return sma_status; } SmaStatus BtrfsMgr::isDifferent(const char *base_snap_, const char *incr_snap_, uint64_t starting_offset_, uint64_t cur_length_, bool &hole, bool &isDiff) { uint8_t *buff1 = NULL, *buff2 = NULL; SmaStatus sma_status = SMA_OK; bool base_eof = false; std::ifstream in_file_base(base_snap_, ios::in | ios::binary); std::ifstream in_file_incr(incr_snap_, ios::in | ios::binary); if (in_file_base.is_open() && in_file_incr.is_open()) { in_file_base.seekg(starting_offset_); buff1 = new uint8_t[cur_length_] (); if (!buff1) { sma_status = SMA_NO_MEM; goto error_out; } in_file_base.read((char *)buff1, cur_length_); if (!in_file_base.good()) { if (in_file_base.eof()) { base_eof = true; } else { sma_status = SMA_IO_ERROR; goto error_out; } } in_file_incr.seekg(starting_offset_); buff2 = new uint8_t[cur_length_] (); if (!buff2) { sma_status = SMA_NO_MEM; goto error_out; } in_file_incr.read((char *)buff2, cur_length_); if (!in_file_incr.good()) { //We do not support shrinking the LUN. assert(!in_file_incr.eof()); sma_status = SMA_IO_ERROR; goto error_out; } isDiff = anyDiff(buff1, buff2, base_eof ? 0 : in_file_base.gcount(), in_file_incr.gcount()); } else { BTRFS_ERR_PRINT("ERROR: failed to open the version file - %s\n", strerror(EIO)); sma_status = SMA_EINVAL; } error_out: if (buff1) { delete [] buff1; } if (buff2) { hole = isHole(buff2, cur_length_); delete [] buff2; } if (in_file_base.is_open()) { in_file_base.close(); } if (in_file_incr.is_open()) { in_file_incr.close(); } return sma_status; } SmaStatus BtrfsMgr::isHoleBaseline(const char *incr_snap, uint64_t starting_offset, uint64_t cur_length, bool &hole) { uint8_t *buff = NULL; SmaStatus sma_status = SMA_OK; std::ifstream in_file_incr(incr_snap, ios::in | ios::binary); if (in_file_incr.is_open()) { in_file_incr.seekg(starting_offset); buff = new uint8_t[cur_length] (); if (!buff) { goto error_out; } in_file_incr.read((char *)buff, cur_length); if (!in_file_incr.good()) { BTRFS_ERR_PRINT("ERROR: failed to open the version file - %s\n", strerror(EIO)); sma_status = SMA_EINVAL; } } else { BTRFS_ERR_PRINT("ERROR: failed to open the version file - %s\n", strerror(EIO)); sma_status = SMA_EINVAL; } error_out: if (buff) { hole = isHole(buff, cur_length); delete [] buff; } if (in_file_incr.is_open()) { in_file_incr.close(); } return sma_status; } bool BtrfsMgr::anyDiff(uint8_t *buff1, uint8_t *buff2, uint64_t length1, uint64_t length2) { uint64_t i; bool diff = false; if (length1 != length2) { diff = true; goto out; } for (i = 0; i < length1; i++) { if (buff1[i] != buff2[i]) { diff = true; break; } } out: return diff; } bool BtrfsMgr::isHole(uint8_t *buff, uint64_t length) { uint64_t i; for (i = 0; i < length; i++) { if (buff[i] != 0) { return false; } } return true; } // Get a list of all the files and directories and store them in a map // with inode number as key and inode_info_t as value. SmaStatus BtrfsMgr::walkTree(string dirname, map &pathList) { DIR *dir; struct dirent *dent; dir = opendir(dirname.c_str()); if (dir == NULL) { BTRFS_ERR_PRINT("Failed to find directory :%s\n", dirname.c_str()); return SMA_INVALID_ARG; } while ((dent = readdir(dir)) != 0) { if ((strcmp(dent->d_name, ".") == 0) || (strcmp(dent->d_name, "..") == 0)) { continue; } std::string fname = (dirname + "/" + dent->d_name); struct stat mystat; unsigned int r = stat(fname.c_str(), &mystat); if (r == 0) { struct inode_info_t inum; inum.inode.inode_num_ = mystat.st_ino; inum.inode.gen_num_ = mystat.st_ino; inum.pathName = fname; pathList[inum.inode.inode_num_] = inum; if (S_ISDIR(mystat.st_mode)) { walkTree(fname, pathList); } } else { BTRFS_ERR_PRINT("Failed to lookup file information " "of :%s\n", fname.c_str()); closedir(dir); return SMA_INTERNAL_ERROR; } } closedir(dir); return SMA_OK; } SmaPlatformType BtrfsMgr::getCurrentPlatformType(std::string vol_name) { SmaPlatformType plat_type = SMA_PLATFORM_TYPE_SF; std::string file_path = btrfs_mount_path_ + "/" + vol_name + "/PLATFORM_TYPE"; std::ifstream in_file; int fsize = get_file_size(file_path); if (!fsize) { BTRFS_ERR_PRINT("Failed to read from PLATFORM_TYPE " "Metafile for volume %s\n", vol_name.c_str()); goto end; } in_file.open(file_path.c_str(), ios::in); if (in_file.is_open()) { string pt; if (!getline(in_file, pt)) { BTRFS_ERR_PRINT("Failed to read from PLATFORM_TYPE " "Metafile for volume %s\n", vol_name.c_str()); goto end; } plat_type = (SmaPlatformType) atoi(pt.c_str()); if (plat_type < 0 || plat_type >= SMA_PLATFORM_TYPE_MAX) { BTRFS_ERR_PRINT("Invalid PLATFORM_TYPE for volume %s. " "Using default platform type (Solid Fire).\n", vol_name.c_str()); plat_type = SMA_PLATFORM_TYPE_SF; } } end: if (in_file.is_open()) { in_file.close(); } return plat_type; }