* [file_utils] spin-off syscall-related file operations 1. Eliminate the potential circular dependency between persistent-data/block.h and persistent-data/file_utils.h, if the former one wants to include the latter. 2. Avoid namespace pollution by removing the "using namespace std" declaration in block.tcc. 3. Correct the header hierarchy: base/xml_utils.h now no longer depends on the higher-level persistent-data/file_utils.h * [file_utils] support block files in get_file_length()
		
			
				
	
	
		
			144 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "base/file_utils.h"
 | |
| 
 | |
| #include "base/error_string.h"
 | |
| 
 | |
| #include <sstream>
 | |
| #include <stdexcept>
 | |
| #include <linux/fs.h>
 | |
| #include <sys/ioctl.h>
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| //----------------------------------------------------------------
 | |
| 
 | |
| // FIXME: give this namespace a name
 | |
| namespace {
 | |
| 	using namespace std;
 | |
| 
 | |
| 	int const DEFAULT_MODE = 0666;
 | |
| 	int const OPEN_FLAGS = O_DIRECT;
 | |
| 
 | |
| 	// FIXME: introduce a new exception for this, or at least lift this
 | |
| 	// to exception.h
 | |
| 	void syscall_failed(char const *call) {
 | |
| 		ostringstream out;
 | |
| 		out << "syscall '" << call << "' failed: " << base::error_string(errno);
 | |
| 		throw runtime_error(out.str());
 | |
| 	}
 | |
| 
 | |
| 	void syscall_failed(string const &call, string const &message)
 | |
| 	{
 | |
| 		ostringstream out;
 | |
| 		out << "syscall '" << call << "' failed: " << base::error_string(errno) << "\n"
 | |
| 		    << message;
 | |
| 		throw runtime_error(out.str());
 | |
| 	}
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------
 | |
| 
 | |
| int
 | |
| file_utils::open_file(string const &path, int flags) {
 | |
| 	int fd = ::open(path.c_str(), OPEN_FLAGS | flags, DEFAULT_MODE);
 | |
| 	if (fd < 0)
 | |
| 		syscall_failed("open",
 | |
| 			       "Note: you cannot run this tool with these options on live metadata.");
 | |
| 
 | |
| 	return fd;
 | |
| }
 | |
| 
 | |
| bool
 | |
| file_utils::file_exists(string const &path) {
 | |
| 	struct ::stat info;
 | |
| 
 | |
| 	int r = ::stat(path.c_str(), &info);
 | |
| 	if (r) {
 | |
| 		if (errno == ENOENT)
 | |
| 			return false;
 | |
| 
 | |
| 		syscall_failed("stat");
 | |
| 		return false; // never get here
 | |
| 
 | |
| 	} else
 | |
| 		return S_ISREG(info.st_mode) || S_ISBLK(info.st_mode);
 | |
| }
 | |
| 
 | |
| void
 | |
| file_utils::check_file_exists(string const &file) {
 | |
| 	struct stat info;
 | |
| 	int r = ::stat(file.c_str(), &info);
 | |
| 	if (r)
 | |
| 		throw runtime_error("Couldn't stat file");
 | |
| 
 | |
| 	if (!S_ISREG(info.st_mode))
 | |
| 		throw runtime_error("Not a regular file");
 | |
| }
 | |
| 
 | |
| int
 | |
| file_utils::create_block_file(string const &path, off_t file_size) {
 | |
| 	if (file_exists(path)) {
 | |
| 		ostringstream out;
 | |
| 		out << __FUNCTION__ << ": file '" << path << "' already exists";
 | |
| 		throw runtime_error(out.str());
 | |
| 	}
 | |
| 
 | |
| 	int fd = open_file(path, O_CREAT | O_EXCL | O_RDWR);
 | |
| 
 | |
| 	int r = ::ftruncate(fd, file_size);
 | |
| 	if (r < 0)
 | |
| 		syscall_failed("ftruncate");
 | |
| 
 | |
| 	return fd;
 | |
| }
 | |
| 
 | |
| int
 | |
| file_utils::open_block_file(string const &path, off_t min_size, bool writeable, bool excl) {
 | |
| 	if (!file_exists(path)) {
 | |
| 		ostringstream out;
 | |
| 		out << __FUNCTION__ << ": file '" << path << "' doesn't exist";
 | |
| 		throw runtime_error(out.str());
 | |
| 	}
 | |
| 
 | |
| 	int flags = writeable ? O_RDWR : O_RDONLY;
 | |
| 	if (excl)
 | |
| 		flags |= O_EXCL;
 | |
| 
 | |
| 	return open_file(path, flags);
 | |
| }
 | |
| 
 | |
| uint64_t
 | |
| file_utils::get_file_length(string const &file) {
 | |
| 	struct stat info;
 | |
| 	uint64_t nr_bytes;
 | |
| 
 | |
| 	int r = ::stat(file.c_str(), &info);
 | |
| 	if (r)
 | |
| 		throw runtime_error("Couldn't stat path");
 | |
| 
 | |
| 	if (S_ISREG(info.st_mode))
 | |
| 		// It's okay to cast st_size to a uint64_t value.
 | |
| 		// If LFS is enabled, st_size should not be negative for regular files.
 | |
| 		nr_bytes = static_cast<uint64_t>(info.st_size);
 | |
| 	else if (S_ISBLK(info.st_mode)) {
 | |
| 		// To get the size of a block device we need to
 | |
| 		// open it, and then make an ioctl call.
 | |
| 		int fd = ::open(file.c_str(), O_RDONLY);
 | |
| 		if (fd < 0)
 | |
| 			throw runtime_error("couldn't open block device to ascertain size");
 | |
| 
 | |
| 		r = ::ioctl(fd, BLKGETSIZE64, &nr_bytes);
 | |
| 		if (r) {
 | |
| 			::close(fd);
 | |
| 			throw runtime_error("ioctl BLKGETSIZE64 failed");
 | |
| 		}
 | |
| 		::close(fd);
 | |
| 	} else
 | |
| 		// FIXME: needs a better message
 | |
| 		throw runtime_error("bad path");
 | |
| 
 | |
| 	return nr_bytes;
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------
 |