/* * s3fs - FUSE-based file system backed by Amazon S3 * * Copyright(C) 2007 Randy Rizun * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------- // Structures and Typedefs //--------------------------------------------------------- struct write_block_part { off_t start; off_t size; }; typedef std::vector wbpart_list_t; typedef std::list strlist_t; //--------------------------------------------------------- // Const //--------------------------------------------------------- static constexpr char usage_string[] = "Usage : \"write_multiblock -f -p \" (allows -f and -p multiple times.)"; //--------------------------------------------------------- // Utility functions //--------------------------------------------------------- static std::unique_ptr create_random_data(off_t size) { int fd; if(-1 == (fd = open("/dev/urandom", O_RDONLY))){ std::cerr << "[ERROR] Could not open /dev/urandom" << std::endl; return nullptr; } std::unique_ptr pbuff(new unsigned char[size]); for(ssize_t readpos = 0, readcnt = 0; readpos < size; readpos += readcnt){ if(-1 == (readcnt = read(fd, &(pbuff[readpos]), static_cast(size - readpos)))){ if(EAGAIN != errno && EWOULDBLOCK != errno && EINTR != errno){ std::cerr << "[ERROR] Failed reading from /dev/urandom with errno: " << errno << std::endl; close(fd); return nullptr; } readcnt = 0; } } close(fd); return pbuff; } static off_t cvt_string_to_number(const char* pstr) { if(!pstr){ return -1; } errno = 0; char* ptemp = nullptr; long long result = strtoll(pstr, &ptemp, 10); if(!ptemp || ptemp == pstr || *ptemp != '\0'){ return -1; } if((result == LLONG_MIN || result == LLONG_MAX) && errno == ERANGE){ return -1; } return static_cast(result); } static bool parse_string(const char* pstr, char delim, strlist_t& strlist) { if(!pstr){ return false; } std::string strAll(pstr); while(!strAll.empty()){ size_t pos = strAll.find_first_of(delim); if(std::string::npos != pos){ strlist.push_back(strAll.substr(0, pos)); strAll = strAll.substr(pos + 1); }else{ strlist.push_back(strAll); strAll.clear(); } } return true; } static bool parse_write_blocks(const char* pstr, wbpart_list_t& wbparts, off_t& max_size) { if(!pstr){ return false; } strlist_t partlist; if(!parse_string(pstr, ',', partlist)){ return false; } for(auto iter = partlist.cbegin(); iter != partlist.cend(); ++iter){ strlist_t partpair; if(parse_string(iter->c_str(), ':', partpair) && 2 == partpair.size()){ write_block_part tmp_part; tmp_part.start = cvt_string_to_number(partpair.front().c_str()); partpair.pop_front(); tmp_part.size = cvt_string_to_number(partpair.front().c_str()); if(tmp_part.start < 0 || tmp_part.size <= 0){ std::cerr << "[ERROR] -p option parameter(" << pstr << ") is something wrong." << std::endl; return false; } max_size = std::max(max_size, tmp_part.size); wbparts.push_back(tmp_part); }else{ std::cerr << "[ERROR] -p option parameter(" << pstr << ") is something wrong." << std::endl; return false; } } return true; } static bool parse_arguments(int argc, char** argv, strlist_t& files, wbpart_list_t& wbparts, off_t& max_size) { if(argc < 2 || !argv){ std::cerr << "[ERROR] The -f option and -p option are required as arguments." << std::endl; std::cerr << usage_string << std::endl; return false; } files.clear(); wbparts.clear(); max_size = 0; int opt; while(-1 != (opt = getopt(argc, argv, "f:p:"))){ switch(opt){ case 'f': files.emplace_back(optarg); break; case 'p': if(!parse_write_blocks(optarg, wbparts, max_size)){ return false; } break; default: std::cerr << usage_string << std::endl; return false; } } if(files.empty() || wbparts.empty()){ std::cerr << "[ERROR] The -f option and -p option are required as arguments." << std::endl; std::cerr << usage_string << std::endl; return false; } return true; } //--------------------------------------------------------- // Main //--------------------------------------------------------- int main(int argc, char** argv) { // parse arguments strlist_t files; wbpart_list_t wbparts; off_t max_size = 0; if(!parse_arguments(argc, argv, files, wbparts, max_size)){ exit(EXIT_FAILURE); } // make data and buffer std::unique_ptr pData = create_random_data(max_size); for(auto fiter = files.cbegin(); fiter != files.cend(); ++fiter){ // open/create file int fd; struct stat st; if(0 == stat(fiter->c_str(), &st)){ if(!S_ISREG(st.st_mode)){ std::cerr << "[ERROR] File " << *fiter << " is existed, but it is not regular file." << std::endl; exit(EXIT_FAILURE); } if(-1 == (fd = open(fiter->c_str(), O_WRONLY))){ std::cerr << "[ERROR] Could not open " << *fiter << std::endl; exit(EXIT_FAILURE); } }else{ if(-1 == (fd = open(fiter->c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644))){ std::cerr << "[ERROR] Could not create " << *fiter << std::endl; exit(EXIT_FAILURE); } } // write blocks for(auto piter = wbparts.cbegin(); piter != wbparts.cend(); ++piter){ // write one block for(ssize_t writepos = 0, writecnt = 0; writepos < piter->size; writepos += writecnt){ if(-1 == (writecnt = pwrite(fd, &(pData[writepos]), static_cast(piter->size - writepos), (piter->start + writepos)))){ if(EAGAIN != errno && EWOULDBLOCK != errno && EINTR != errno){ std::cerr << "[ERROR] Failed writing to " << *fiter << " by errno : " << errno << std::endl; close(fd); exit(EXIT_FAILURE); } writecnt = 0; } } } // close file close(fd); } exit(EXIT_SUCCESS); } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: expandtab sw=4 ts=4 fdm=marker * vim<600: expandtab sw=4 ts=4 */