1/* 2 * XFS preallocation support module. 3 * 4 * Copyright (c) James Peach 2006 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "includes.h" 21 22/* Extent preallocation module. 23 * 24 * The purpose of this module is to preallocate space on the filesystem when 25 * we have a good idea of how large files are supposed to be. This lets writes 26 * proceed without having to allocate new extents and results in better file 27 * layouts on disk. 28 * 29 * Currently only implemented for XFS. This module is based on an original idea 30 * and implementation by Sebastian Brings. 31 * 32 * Tunables. 33 * 34 * prealloc: <ext> Number of bytes to preallocate for a file with 35 * the matching extension. 36 * prealloc:debug Debug level at which to emit messages. 37 * 38 * Example. 39 * 40 * prealloc:mpeg = 500M # Preallocate *.mpeg to 500 MiB. 41 */ 42 43#ifdef HAVE_XFS_LIBXFS_H 44#include <xfs/libxfs.h> 45#define lock_type xfs_flock64_t 46#else 47#define lock_type struct flock64 48#endif 49 50#ifdef HAVE_GPFS 51#include "gpfs_gpl.h" 52#endif 53 54#define MODULE "prealloc" 55static int module_debug; 56 57static int preallocate_space(int fd, SMB_OFF_T size) 58{ 59 int err; 60#ifndef HAVE_GPFS 61 lock_type fl = {0}; 62 63 if (size <= 0) { 64 return 0; 65 } 66 67 fl.l_whence = SEEK_SET; 68 fl.l_start = 0; 69 fl.l_len = size; 70 71 /* IMPORTANT: We use RESVSP because we want the extents to be 72 * allocated, but we don't want the allocation to show up in 73 * st_size or persist after the close(2). 74 */ 75 76#if defined(XFS_IOC_RESVSP64) 77 /* On Linux this comes in via libxfs.h. */ 78 err = xfsctl(NULL, fd, XFS_IOC_RESVSP64, &fl); 79#elif defined(F_RESVSP64) 80 /* On IRIX, this comes from fcntl.h. */ 81 err = fcntl(fd, F_RESVSP64, &fl); 82#else 83 err = -1; 84 errno = ENOSYS; 85#endif 86#else /* GPFS uses completely different interface */ 87 err = gpfs_prealloc(fd, (gpfs_off64_t)0, (gpfs_off64_t)size); 88#endif 89 90 if (err) { 91 DEBUG(module_debug, 92 ("%s: preallocate failed on fd=%d size=%lld: %s\n", 93 MODULE, fd, (long long)size, strerror(errno))); 94 } 95 96 return err; 97} 98 99static int prealloc_connect( 100 struct vfs_handle_struct * handle, 101 const char * service, 102 const char * user) 103{ 104 int ret = SMB_VFS_NEXT_CONNECT(handle, service, user); 105 106 if (ret < 0) { 107 return ret; 108 } 109 110 module_debug = lp_parm_int(SNUM(handle->conn), 111 MODULE, "debug", 100); 112 113 return 0; 114} 115 116static int prealloc_open(vfs_handle_struct* handle, 117 struct smb_filename *smb_fname, 118 files_struct * fsp, 119 int flags, 120 mode_t mode) 121{ 122 int fd; 123 off64_t size = 0; 124 125 const char * dot; 126 char fext[10]; 127 128 if (!(flags & (O_CREAT|O_TRUNC))) { 129 /* Caller is not intending to rewrite the file. Let's not mess 130 * with the allocation in this case. 131 */ 132 goto normal_open; 133 } 134 135 *fext = '\0'; 136 dot = strrchr(smb_fname->base_name, '.'); 137 if (dot && *++dot) { 138 if (strlen(dot) < sizeof(fext)) { 139 strncpy(fext, dot, sizeof(fext)); 140 strnorm(fext, CASE_LOWER); 141 } 142 } 143 144 if (*fext == '\0') { 145 goto normal_open; 146 } 147 148 /* Syntax for specifying preallocation size is: 149 * MODULE: <extension> = <size> 150 * where 151 * <extension> is the file extension in lower case 152 * <size> is a size like 10, 10K, 10M 153 */ 154 size = conv_str_size(lp_parm_const_string(SNUM(handle->conn), MODULE, 155 fext, NULL)); 156 if (size <= 0) { 157 /* No need to preallocate this file. */ 158 goto normal_open; 159 } 160 161 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); 162 if (fd < 0) { 163 return fd; 164 } 165 166 /* Prellocate only if the file is being created or replaced. Note that 167 * Samba won't ever pass down O_TRUNC, which is why we have to handle 168 * truncate calls specially. 169 */ 170 if ((flags & O_CREAT) || (flags & O_TRUNC)) { 171 SMB_OFF_T * psize; 172 173 psize = VFS_ADD_FSP_EXTENSION(handle, fsp, SMB_OFF_T, NULL); 174 if (psize == NULL || *psize == -1) { 175 return fd; 176 } 177 178 DEBUG(module_debug, 179 ("%s: preallocating %s (fd=%d) to %lld bytes\n", 180 MODULE, smb_fname_str_dbg(smb_fname), fd, 181 (long long)size)); 182 183 *psize = size; 184 if (preallocate_space(fd, *psize) < 0) { 185 VFS_REMOVE_FSP_EXTENSION(handle, fsp); 186 } 187 } 188 189 return fd; 190 191normal_open: 192 /* We are not creating or replacing a file. Skip the 193 * preallocation. 194 */ 195 DEBUG(module_debug, ("%s: skipping preallocation for %s\n", 196 MODULE, smb_fname_str_dbg(smb_fname))); 197 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); 198} 199 200static int prealloc_ftruncate(vfs_handle_struct * handle, 201 files_struct * fsp, 202 SMB_OFF_T offset) 203{ 204 SMB_OFF_T *psize; 205 int ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset); 206 207 /* Maintain the allocated space even in the face of truncates. */ 208 if ((psize = VFS_FETCH_FSP_EXTENSION(handle, fsp))) { 209 preallocate_space(fsp->fh->fd, *psize); 210 } 211 212 return ret; 213} 214 215static struct vfs_fn_pointers prealloc_fns = { 216 .open = prealloc_open, 217 .ftruncate = prealloc_ftruncate, 218 .connect_fn = prealloc_connect, 219}; 220 221NTSTATUS vfs_prealloc_init(void); 222NTSTATUS vfs_prealloc_init(void) 223{ 224 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, 225 MODULE, &prealloc_fns); 226} 227