// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * Copyright (C) 2014 Red Hat * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. * */ #include #include #include #include // from include/linux/falloc.h: #ifndef FALLOC_FL_PUNCH_HOLE # define FALLOC_FL_PUNCH_HOLE 0x2 #endif #include "FS.h" #include "acconfig.h" #ifdef HAVE_LIBXFS #include "XFS.h" #endif #if defined(DARWIN) || defined(__FreeBSD__) #include #else #include #endif #include "include/compat.h" // --------------- FS *FS::create(uint64_t f_type) { switch (f_type) { #ifdef HAVE_LIBXFS case XFS_SUPER_MAGIC: return new XFS; #endif default: return new FS; } } FS *FS::create_by_fd(int fd) { struct statfs st; ::fstatfs(fd, &st); return create(st.f_type); } // --------------- int FS::set_alloc_hint(int fd, uint64_t hint) { return 0; // no-op } #ifdef HAVE_NAME_TO_HANDLE_AT int FS::get_handle(int fd, std::string *h) { char buf[sizeof(struct file_handle) + MAX_HANDLE_SZ]; struct file_handle *fh = (struct file_handle *)buf; int mount_id; fh->handle_bytes = MAX_HANDLE_SZ; int r = name_to_handle_at(fd, "", fh, &mount_id, AT_EMPTY_PATH); if (r < 0) { return -errno; } *h = std::string(buf, fh->handle_bytes + sizeof(struct file_handle)); return 0; } int FS::open_handle(int mount_fd, const std::string& h, int flags) { if (h.length() < sizeof(struct file_handle)) { return -EINVAL; } struct file_handle *fh = (struct file_handle *)h.data(); if (fh->handle_bytes > h.length()) { return -ERANGE; } int fd = open_by_handle_at(mount_fd, fh, flags); if (fd < 0) return -errno; return fd; } #else // HAVE_NAME_TO_HANDLE_AT int FS::get_handle(int fd, std::string *h) { return -EOPNOTSUPP; } int FS::open_handle(int mount_fd, const std::string& h, int flags) { return -EOPNOTSUPP; } #endif // HAVE_NAME_TO_HANDLE_AT int FS::copy_file_range(int to_fd, uint64_t to_offset, int from_fd, uint64_t from_offset, uint64_t from_len) { assert(0 == "write me"); } int FS::zero(int fd, uint64_t offset, uint64_t length) { int r; /* From the fallocate(2) man page: Specifying the FALLOC_FL_PUNCH_HOLE flag (available since Linux 2.6.38) in mode deallocates space (i.e., creates a hole) in the byte range starting at offset and continuing for len bytes. Within the specified range, partial filesystem blocks are zeroed, and whole filesystem blocks are removed from the file. After a successful call, subsequent reads from this range will return zeroes. The FALLOC_FL_PUNCH_HOLE flag must be ORed with FALLOC_FL_KEEP_SIZE in mode; in other words, even when punching off the end of the file, the file size (as reported by stat(2)) does not change. Not all filesystems support FALLOC_FL_PUNCH_HOLE; if a filesystem doesn't support the operation, an error is returned. The operation is supported on at least the following filesystems: * XFS (since Linux 2.6.38) * ext4 (since Linux 3.0) * Btrfs (since Linux 3.7) * tmpfs (since Linux 3.5) So: we only do this is PUNCH_HOLE *and* KEEP_SIZE are defined. */ #if !defined(DARWIN) && !defined(__FreeBSD__) # ifdef CEPH_HAVE_FALLOCATE # ifdef FALLOC_FL_KEEP_SIZE // first try fallocate r = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, length); if (r < 0) { r = -errno; } if (r != -EOPNOTSUPP) { goto out; // a real error } // if that failed (-EOPNOTSUPP), fall back to writing zeros. # endif # endif #endif { // fall back to writing zeros bufferlist bl; bl.append_zero(length); r = ::lseek64(fd, offset, SEEK_SET); if (r < 0) { r = -errno; goto out; } r = bl.write_fd(fd); } out: return r; } // ---------------