// TODO add copyright #include #include #include #include #include #include #include #include #include #include // #define _S3_DEVEL_ 1 #ifdef _S3_DEVEL_ #include #endif #include "s3_local.h" #include "s3_mgr.h" using std::ios; static int s3_create_subvolume(const char *subvol_path); S3Mgr *S3Mgr::instance_; std::string S3Mgr::s3_mount_path_; S3Mgr::S3Mgr(std::string s3_mount_path) { // set the base vol path base_vol_path_ = s3_mount_path; mtdt_vol_path_ = base_vol_path_ + "/mtdt_vol"; snap_vol_path_ = base_vol_path_ + "/snap_vol"; vol_dir_ = "VOL"; snap_dir_ = "SNAP"; tag_dir_ = "TAG"; opaque_fn_ = "opaque"; // create the mtdt subvolume int res = s3_create_subvolume(mtdt_vol_path_.c_str()); if (res) { assert(0); } // create the snap_vol subvolume res = s3_create_subvolume(snap_vol_path_.c_str()); if (res) { assert(0); } } // get the base subvolume path std::string S3Mgr::get_base_path() { return base_vol_path_; } // get absolute path for a given vol_id std::string S3Mgr::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 S3Mgr::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 S3Mgr::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 S3Mgr::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; } bool S3Mgr::is_rw_volume(std::string vol_path) { struct stat buffer; std::string path = this->get_base_path(); path += "/" + vol_path + "/read_write"; return (stat (path.c_str(), &buffer) == 0); } // get the file size uint32_t S3Mgr::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 S3Mgr::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 { S3_ERR_PRINT("Failed to open file %s\n", file_path.c_str()); // return the error ret = SMA_NOENT; } return ret; } // store vol_id to uuid mapping SmaStatus S3Mgr::store_uuid(void *vol_id, uuid_t *ep_uuid) { SmaStatus ret = SMA_OK; assert(vol_id); assert(ep_uuid); // 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 file_path = mtdt_vol_path_; std::string file_name(str); file_path += "/" + file_name; char uuid[UUID_SIZE]; memcpy(uuid, ep_uuid, UUID_SIZE); std::ofstream out_file(file_path.c_str(), ios::out | ios::binary | ios::trunc); if (out_file.is_open ()) { out_file.write(uuid, UUID_SIZE); out_file.close(); } else { // return the error ret = SMA_NOENT; } return ret; } // fetch uuid for a given vol_id SmaStatus S3Mgr::get_uuid(void *vol_id, uuid_t *ep_uuid) { SmaStatus ret = SMA_OK; assert(vol_id); assert(ep_uuid); // 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 file_path = mtdt_vol_path_; std::string file_name(str); file_path += "/" + file_name; // buffer for uuid char uuid_str[UUID_SIZE]; std::ifstream in_file(file_path.c_str(), ios::in | ios::binary); if (in_file.is_open()) { in_file.read(uuid_str, UUID_SIZE); in_file.close(); // create the uuid memcpy(ep_uuid, uuid_str, UUID_SIZE); } else { // return the error ret = SMA_NOENT; } return ret; } SmaStatus S3Mgr::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) { S3_ERR_PRINT("mkdir %s failed with error %d\n", path, errno); // return the error ret = SMA_NOENT; } } else if (!S_ISDIR(st.st_mode)) { S3_ERR_PRINT("%s is not a DIR\n", path); errno = ENOTDIR; // return the error ret = SMA_NOENT; } return ret; } // store vol_info record SmaStatus S3Mgr::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 S3Mgr::remove_vol_info(void *vol_id, uint32_t type_no, SmaOpaqueVolumeInfo *vol_info) { SmaStatus ret = SMA_NOENT; 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) { S3_PRINT("opaque file %s deleted\n", file_path.c_str()); ret = SMA_OK; } } return ret; } // read vol_info record SmaStatus S3Mgr::read_vol_info(void *vol_id, uint32_t type_no, SmaOpaqueVolumeInfo *vol_info) { SmaStatus ret = SMA_NOENT; 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 s3_create_subvolume(const char *subvol_path) { int retval = 0, res; int fddst = -1; char *dupname = NULL; char *dupdir = NULL; char *newname; char *dstdir; DIR *dirstream = NULL; #ifdef _S3_DEVEL_ retval = 1; /* failure */ res = test_isdir(subvol_path); if (res < 0 && res != -ENOENT) { S3_ERR_PRINT("cannot access %s: %s\n", subvol_path, strerror(-res)); goto out; } if (res >= 0) { S3_DEBUG_PRINT("target path already exists: %s\n", subvol_path); retval = 0; goto out; } dupname = strdup(subvol_path); newname = basename(dupname); dupdir = strdup(subvol_path); dstdir = dirname(dupdir); fddst = open_subvol_dir(dstdir, &dirstream); if (fddst < 0) { goto out; } S3_PRINT("Create subvolume '%s/%s'\n", dstdir, newname); struct btrfs_ioctl_vol_args args; memset(&args, 0, sizeof(args)); strncpy(args.name, newname, sizeof(args.name)); args.name[sizeof(args.name) - 1] = '\0'; res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args); if (res < 0) { S3_ERR_PRINT("cannot create subvolume: %s\n", strerror(errno)); goto out; } retval = 0; /* success */ out: if (dirstream) { closedir(dirstream); free(dupname); free(dupdir); } #endif // _S3_DEVEL_ return retval; } // create snapshot for given subvolume SmaStatus S3Mgr::create_snapshot(void *vol_id, SmaSnapInfo *snap_info) { SmaStatus status = SMA_NOENT; #ifdef _S3_DEVEL_ assert(snap_info->name_); std::string subvol_src = get_full_path(vol_id); std::string subvol_snap_root = get_snapvol_path(vol_id); std::string snapname(snap_info->name_); // create the snapshot with following path // snap root is present at // /btrfs/snap_vol/vol_id // snapshot is present at // /btrfs/snap_vol/vol_id/snap_info->name_ // create the subvolume snap root if it doesnt exists // create the /btrfs/snap_vol/vol_id subvolume int res = s3_create_subvolume(subvol_snap_root.c_str()); if (res) { return status; } // TODO verify that the snap dir doesn't exist DIR *dirstream1 = NULL; DIR *dirstream2 = NULL; int fd = open_subvol_dir(subvol_src.c_str(), &dirstream1); if (fd < 0) { S3_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) { S3_ERR_PRINT("Failed to open snap %s dir\n", subvol_snap_root.c_str()); closedir(dirstream1); return status; } struct btrfs_ioctl_vol_args_v2 args; memset(&args, 0, sizeof(args)); args.flags |= BTRFS_SUBVOL_RDONLY; S3_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'; std::string snap_dir_path = subvol_snap_root + "/" + snapname; res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args); if (res < 0) { S3_ERR_PRINT("snapshot create failed '%s': %s\n", snapname.c_str(), strerror(errno)); goto out; } else { // set the snap_id uint32_t snap_id_sz = MAX_SNAPID_SIZE; uint64_t snap_id = get_snapvol_id(vol_id, snapname); assert(snap_id); // assumption is that snap_id is allocated by caller memcpy(snap_info->snap_id_, &snap_id, snap_id_sz); // set the snap create time struct stat st; res = stat(snap_dir_path.c_str(), &st); if (res < 0) { status = SMA_NOENT; goto out; } else { struct timespec ctime = st.st_ctim; snap_info->create_time_ = ctime.tv_sec; } status = SMA_OK; /* success */ } out: if (dirstream1) closedir(dirstream1); if (dirstream2) closedir(dirstream2); #endif // _S3_DEVEL_ return status; } #if 0 SmaStatus S3Mgr::store_snap_create_info(void *vol_id, SmaSnapInfo *snap_info) { SmaStatus status = SMA_OK; // we need to store the snapshot create time // we store this info at following path // /btrfs/mtdt_vol/vol_id/snap_id : it contains snap create time std::string dir_path = get_mtdt_vol_path(vol_id); // create the /btrfs/mtdt_vol/vol_id dir entry if it doesnt exist status = create_dir(dir_path.c_str()); if (status != SMA_OK) { return status; } // write the create time in the snap_id file uint32_t buf_sz = sizeof(snap_info->create_time_); char *buffer = new char[buf_sz]; // populate the buffer uint32_t len = sizeof(snap_info->create_time_); memcpy(buffer, &snap_info->create_time_, len); std::string snap_id(snap_info->name_); status = write_to_file(dir_path, snap_id, buffer, len); delete buffer; return status; } #endif // store snap_info record SmaStatus S3Mgr::store_snap_info(void *vol_id, uint32_t type_no, SmaOpaqueSnapInfo *snap_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/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 the /btrfs/mtdt_vol/vol_id/SNAP/type_no/snap_id dir entry // if it doesnt exist std::string snap_id; std::ostringstream sid_convert; sid_convert << (*(uint64_t *)(snap_info->snap_id_)); snap_id = sid_convert.str(); dir_path += "/" + snap_id; ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // write the snap_info to the fs uint32_t buf_sz = sizeof(snap_info->opaque_size_) + snap_info->opaque_size_; char *buffer = new char[buf_sz](); // populate the buffer uint32_t off = 0; uint32_t len = sizeof(snap_info->opaque_size_); uint32_t total_len = len; memcpy(buffer+off, &snap_info->opaque_size_, len); off = len; len = snap_info->opaque_size_; total_len += len; memcpy(buffer+off, snap_info->opaque_, len); ret = write_to_file(dir_path, opaque_fn_, buffer, total_len); delete[] buffer; return ret; } // delete snap_info record SmaStatus S3Mgr::remove_snap_info(void *vol_id, uint32_t type_no, SmaOpaqueSnapInfo *snap_info) { SmaStatus ret = SMA_NOENT; 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/SNAP/type_no/snap_info->snap_id_/opaque_fn_ dir_path += "/"; dir_path += snap_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; // append snap_id_ std::string snap_id; std::ostringstream sid_convert; sid_convert << (*(uint64_t *)(snap_info->snap_id_)); snap_id = sid_convert.str(); dir_path += "/" + snap_id; // find the opaque file 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) { S3_PRINT("opaque file %s deleted\n", file_path.c_str()); ret = SMA_OK; } } return ret; } // read snap_info dir to get list of snap_ids int S3Mgr::read_snap_info_dir(void *vol_id, uint32_t type_no, std::vector &snap_id_vec) { int ret = 0; assert(vol_id); std::string dir_path = get_mtdt_vol_path(vol_id); // read the snap_info dir with the following path get all snap_ids // /btrfs/mtdt_vol/vol_id/SNAP/type_no/ dir_path += "/"; dir_path += snap_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; // perform the actual read DIR *dir = NULL; struct dirent *ent = NULL; if ((dir = opendir (dir_path.c_str())) != NULL) { /* print all the files and directories within directory */ while ((ent = readdir (dir)) != NULL) { S3_DEBUG_PRINT("%s\n", ent->d_name); std::string name(ent->d_name); if (name == "." || name == "..") { continue; } std::string fname(ent->d_name, strlen(ent->d_name)); std::istringstream iss(fname); uint64_t *snap_id = new uint64_t; *snap_id = 0; iss >> *snap_id; snap_id_vec.push_back(snap_id); } closedir (dir); } else { /* could not open directory */ S3_DEBUG_PRINT("Could not open dir %s\n", dir_path.c_str()); // ret = -1; } return ret; } // read each snap_info record int S3Mgr::read_snap_info_entry(void *vol_id, uint32_t type_no, SmaOpaqueSnapInfo *snap_info) { int ret = -1; 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/SNAP/type_no/snap_info->snap_id_/opaque_fn_ dir_path += "/"; dir_path += snap_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; // append snap_id_ to path std::string str; std::ostringstream sid_convert; sid_convert << (*(uint64_t *)(snap_info->snap_id_)); str = sid_convert.str(); dir_path += "/" + str; // access the file 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 snap_info std::ifstream in_file(file_path.c_str(), ios::in | ios::binary); if (in_file.is_open()) { in_file.read((char *)&snap_info->opaque_size_, sizeof(snap_info->opaque_size_)); // allocate the memory for opaque_ snap_info->opaque_ = new uint8_t[snap_info->opaque_size_](); // read the payload in_file.read((char *)snap_info->opaque_, snap_info->opaque_size_); in_file.close(); } // verify if expected data is same as file size uint32_t rd_size = sizeof(snap_info->opaque_size_) + snap_info->opaque_size_; assert(fsize == rd_size); ret = 0; return ret; } // store tag_info record int S3Mgr::store_tag_info(void *vol_id, uint32_t type_no, void *snap_id, SmaOpaqueTagInfo *tag_info) { int ret = 0; 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/TAG dir entry if it doesnt exist dir_path += "/"; dir_path += tag_dir_; ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // create the /btrfs/mtdt_vol/vol_id/TAG/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 the /btrfs/mtdt_vol/vol_id/TAG/type_no/snap_id dir entry // if it doesnt exist std::string snap_str; std::ostringstream sid_convert; sid_convert << (*(uint64_t *)(snap_id)); snap_str = sid_convert.str(); dir_path += "/" + snap_str; ret = create_dir(dir_path.c_str()); if (ret != SMA_OK) { return ret; } // use key_ as filename to create one std::string opq_file_name((char *)tag_info->key_); // write the tag_info to the fs uint32_t buf_sz = sizeof(tag_info->key_size_) + tag_info->key_size_ + sizeof(tag_info->opaque_size_) + tag_info->opaque_size_; char *buffer = new char[buf_sz](); // populate the buffer uint32_t off = 0; uint32_t len = sizeof(tag_info->key_size_); uint32_t total_len = len; memcpy(buffer+off, &tag_info->key_size_, len); off = total_len; len = tag_info->key_size_; total_len += len; memcpy(buffer+off, tag_info->key_, len); off = total_len; len = sizeof(tag_info->opaque_size_); total_len += len; memcpy(buffer+off, &tag_info->opaque_size_, len); off = total_len; len = tag_info->opaque_size_; total_len += len; memcpy(buffer+off, tag_info->opaque_, len); ret = write_to_file(dir_path, opq_file_name, buffer, total_len); delete[] buffer; return ret; } // delete tag_info record int S3Mgr::remove_tag_info(void *vol_id, uint32_t type_no, void *snap_id, SmaOpaqueTagInfo *tag_info) { int ret = -1; 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/TAG/type_no/snap_id/tag_info->key_ dir_path += "/"; dir_path += tag_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; // append snap_id std::string snap_id_str; std::ostringstream sid_convert; sid_convert << (*(uint64_t *)(snap_id)); snap_id_str = sid_convert.str(); dir_path += "/" + snap_id_str; // find the key file std::string key_filename((char *)tag_info->key_); std::string file_path = dir_path + "/" + key_filename; std::ifstream in_file(file_path.c_str()); if (in_file) { if (remove(file_path.c_str()) == 0) { S3_PRINT("key file %s deleted\n", file_path.c_str()); ret = 0; } } return ret; } // read tag_info dir to get list of tag files int S3Mgr::read_tag_info_dir(void *vol_id, uint32_t type_no, void *snap_id, std::vector &key_fname_vec) { int res = 0; assert(vol_id); std::string dir_path = get_mtdt_vol_path(vol_id); // read the tag_info dir with the following path get all key files // /btrfs/mtdt_vol/vol_id/TAG/type_no/snap_id dir_path += "/"; dir_path += tag_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; // append snap_id to path std::string str; std::ostringstream sid_convert; sid_convert << (*(uint64_t *)(snap_id)); str = sid_convert.str(); dir_path += "/" + str; // perform the actual read DIR *dir = NULL; struct dirent *ent = NULL; if ((dir = opendir (dir_path.c_str())) != NULL) { /* print all the files and directories within directory */ while ((ent = readdir (dir)) != NULL) { S3_DEBUG_PRINT("%s\n", ent->d_name); std::string name(ent->d_name); if (name == "." || name == "..") { continue; } uint32_t key_fn_sz = strlen(ent->d_name) + 1; char *key_fn = new char[key_fn_sz](); strncpy(key_fn, ent->d_name, key_fn_sz); key_fn[key_fn_sz - 1] = '\0'; key_fname_vec.push_back(key_fn); } closedir (dir); } else { /* could not open directory */ S3_DEBUG_PRINT("Could not open dir %s\n", dir_path.c_str()); // res = -1; } return res; } // read each tag_info record int S3Mgr::read_tag_info_entry(void *vol_id, uint32_t type_no, void *snap_id, char *file_name, SmaOpaqueTagInfo *tag_info) { int res = 0; assert(vol_id); std::string dir_path = get_mtdt_vol_path(vol_id); // read the tag_info_entry from the following path // /btrfs/mtdt_vol/vol_id/TAG/type_no/snap_id/file_name dir_path += "/"; dir_path += tag_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; // append snap_id to path std::string str; std::ostringstream sid_convert; sid_convert << (*(uint64_t *)(snap_id)); str = sid_convert.str(); dir_path += "/" + str; // access the file std::string filename(file_name); std::string file_path = dir_path + "/" + filename; // check if file exists and find its size uint32_t fsize = get_file_size(file_path); if (!fsize) { // return the error return res; } // lets read the data and populate tag_info std::ifstream in_file(file_path.c_str(), ios::in | ios::binary); if (in_file.is_open()) { in_file.read((char *)&tag_info->key_size_, sizeof(tag_info->key_size_)); // allocate the memory for key_ tag_info->key_ = new uint8_t[tag_info->key_size_](); // read the payload in_file.read((char *)tag_info->key_, tag_info->key_size_); in_file.read((char *)&tag_info->opaque_size_, sizeof(tag_info->opaque_size_)); // allocate the memory for opaque_ tag_info->opaque_ = new uint8_t[tag_info->opaque_size_](); // read the payload in_file.read((char *)tag_info->opaque_, tag_info->opaque_size_); in_file.close(); } // verify if expected data is same as file size uint32_t rd_size = sizeof(tag_info->key_size_) + tag_info->key_size_ + sizeof(tag_info->opaque_size_) + tag_info->opaque_size_; assert(fsize == rd_size); res = 0; return res; } // s3 specific routines #ifdef _S3_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) { S3_ERR_PRINT("ERROR: can't perform the search - %s\n", strerror(errno)); return ret; } *treeid = args.treeid; return 0; } 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 = S3_ROOT_BACKREF_KEY; sk->min_type = S3_ROOT_ITEM_KEY; sk->max_type = (uint32_t)-1; sk->min_type = 0; //sk->min_objectid = S3_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; ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args); if (ret < 0) { return ret; } /* the ioctl returns the number of item it found in nr_items */ if (sk->nr_items == 0) { return ret; } // TODO implement this in a loop if we need to support more than 4096 // snapshots assert(sk->nr_items <= 4096); 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 == S3_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); } } off += sh.len; /* * record the mins in sk so we can make sure the * next search doesn't repeat this root */ sk->min_objectid = sh.objectid; sk->min_type = sh.type; sk->min_offset = sh.offset; } return 0; } #endif // ifdef _S3_DEVEL_ #include SmaStatus S3Mgr::populate_snap_list(SmaSnapListInfo *snap_list, std::vector snap_names, std::vector snap_ids, std::string subvol_snap_root) { SmaStatus status = SMA_OK; std::reverse(snap_names.begin(), snap_names.end()); std::reverse(snap_ids.begin(), snap_ids.end()); // if selective_ is false if (snap_list->selective_ == false) { assert(!snap_list->count_); assert(snap_names.size()); snap_list->count_ = snap_names.size(); snap_list->entries_ = new SmaSnapInfo[snap_list->count_](); for (uint32_t i = 0; i < snap_list->count_; i++) { snap_list->entries_[i].name_ = NULL; snap_list->entries_[i].snap_id_ = NULL; snap_list->entries_[i].create_time_ = 0; } } else { assert(snap_list->count_); assert(snap_list->entries_); } for (uint32_t i = 0; i < snap_list->count_; i++) { std::string snap_name = snap_names[i]; uint64_t s3_snap_id = snap_ids[i]; uint32_t size = snap_name.size() + 1; struct stat st; if (snap_list->entries_[i].snap_id_) { // this is set if selective_ is set to true uint64_t snap_id = (*(uint64_t *)snap_list->entries_[i].snap_id_); if (snap_id != s3_snap_id) { // snap_id is different than snap_name // so keep looking till we find a match continue; } } else { // set the snap_id_ uint64_t *snap_id_ptr = new uint64_t; *snap_id_ptr = s3_snap_id; snap_list->entries_[i].snap_id_ = snap_id_ptr; } // set name_ snap_list->entries_[i].name_ = new char[size](); strncpy(snap_list->entries_[i].name_, snap_name.c_str(), size); snap_list->entries_[i].name_[size - 1] = '\0'; // get the snap create time std::string snap_dir_path = subvol_snap_root + "/" + snap_name; int ret = stat(snap_dir_path.c_str(), &st); if (ret < 0) { status = SMA_NOENT; break; } else { struct timespec ctime = st.st_ctim; snap_list->entries_[i].create_time_ = ctime.tv_sec; } } return status; } // get snap list SmaStatus S3Mgr::get_snap_list(void *vol_id, SmaSnapListInfo *snap_list) { SmaStatus status = SMA_OK; #ifdef _S3_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) { S3_PRINT("Found NO snapshots for vol_id: %s\n", (char *)vol_id); return status; } 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) { S3_ERR_PRINT("can't get rootid for '%s'\n", subvol_snap_root.c_str()); status = SMA_NOENT; goto out; } list_subvol_search(fd, top_id, snap_name_list, snap_id_list); if (!snap_name_list.size()) { S3_PRINT("Found NO snapshots for vol_id: %s\n", (char *)vol_id); goto out; } // populate the snap_list from snap_name_list populate_snap_list(snap_list, snap_name_list, snap_id_list, subvol_snap_root); out: closedir(dirstream1); #endif // _S3_DEVEL_ return status; } // get snapshot subvolume id uint64_t S3Mgr::get_snapvol_id(void *vol_id, std::string snap_name) { uint64_t top_id = 0; #ifdef _S3_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) { S3_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) { S3_ERR_PRINT("can't get rootid for '%s'", snapvol_root.c_str()); top_id = 0; } closedir(dirstream1); #endif // _S3_DEVEL_ return top_id; } // get the snapshot subvolume name for a given vol_id, snap_id std::string S3Mgr::get_snapvol_name(void *vol_id, void *snap_id) { std::string snapname; #ifdef _S3_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) { S3_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) { S3_ERR_PRINT("can't get rootid for '%s'\n", subvol_snap_root.c_str()); goto out; } list_subvol_search(fd, top_id, snap_name_list, snap_id_list); if (!snap_name_list.size()) { S3_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 s3_snap_id = snap_id_list[i]; if (req_snap_id == s3_snap_id) { // we found the requested snap_id snapname = subvol_snap_root + "/" + snap_name; break; } } out: closedir(dirstream1); #endif return snapname; } #ifdef _S3_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 S3Mgr::delete_snapshot(void *vol_id, void *snap_id) { SmaStatus status = SMA_NOENT; #if 0 #ifdef _S3_DEVEL_ int res, ret = 0; int fd = -1; struct btrfs_ioctl_vol_args args; char *dname, *vname; char *dupdname = NULL; char *dupvname = NULL; char *path; DIR *dirstream = NULL; int verbose = 0; int commit_mode = 0; // path = // TODO test that the path is subvolume dupdname = strdup(path); dname = dirname(dupdname); dupvname = strdup(path); vname = basename(dupvname); fd = open_subvol_dir(dname, &dirstream); if (fd < 0) { ret = 1; goto out; } S3_PRINT("Delete subvolume (%s): '%s/%s'\n", commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc) ? "commit" : "no-commit", dname, vname); memset(&args, 0, sizeof(args)); strncpy_null(args.name, vname); res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args); if(res < 0 ){ S3_ERR_PRINT("cannot delete '%s/%s': %s", dname, vname, strerror(errno)); ret = 1; goto out; } res = wait_for_commit(fd); if (res < 0) { error("unable to wait for commit after '%s': %s", path, strerror(errno)); ret = 1; } out: free(dupdname); free(dupvname); dupdname = NULL; dupvname = NULL; close_file_or_dir(fd, dirstream); #endif // _S3_DEVEL_ #endif // 0 return status; }