| 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"
 | 
|---|
| 55 | static int module_debug;
 | 
|---|
| 56 | 
 | 
|---|
| 57 | static 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 | 
 | 
|---|
| 99 | static 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 | 
 | 
|---|
| 116 | static 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 | 
 | 
|---|
| 191 | normal_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 | 
 | 
|---|
| 200 | static 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 | 
 | 
|---|
| 215 | static struct vfs_fn_pointers prealloc_fns = {
 | 
|---|
| 216 |         .open = prealloc_open,
 | 
|---|
| 217 |         .ftruncate = prealloc_ftruncate,
 | 
|---|
| 218 |         .connect_fn = prealloc_connect,
 | 
|---|
| 219 | };
 | 
|---|
| 220 | 
 | 
|---|
| 221 | NTSTATUS vfs_prealloc_init(void);
 | 
|---|
| 222 | NTSTATUS vfs_prealloc_init(void)
 | 
|---|
| 223 | {
 | 
|---|
| 224 |         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
 | 
|---|
| 225 |                                 MODULE, &prealloc_fns);
 | 
|---|
| 226 | }
 | 
|---|