vfs_btrfs: fix copy-chunk dest unlock args
[samba.git] / source3 / modules / vfs_btrfs.c
1 /*
2  * Module to make use of awesome Btrfs features
3  *
4  * Copyright (C) David Disseldorp 2011-2013
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 <linux/ioctl.h>
21 #include <linux/fs.h>
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include "includes.h"
28 #include "smbd/smbd.h"
29 #include "librpc/gen_ndr/smbXsrv.h"
30 #include "librpc/gen_ndr/ioctl.h"
31 #include "lib/util/tevent_ntstatus.h"
32
33 struct btrfs_ioctl_clone_range_args {
34         int64_t src_fd;
35         uint64_t src_offset;
36         uint64_t src_length;
37         uint64_t dest_offset;
38 };
39
40 #define BTRFS_IOCTL_MAGIC 0x94
41 #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
42                                    struct btrfs_ioctl_clone_range_args)
43
44 struct btrfs_cc_state {
45         struct vfs_handle_struct *handle;
46         off_t copied;
47         struct tevent_req *subreq;      /* non-null if passed to next VFS fn */
48 };
49 static void btrfs_copy_chunk_done(struct tevent_req *subreq);
50
51 static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle,
52                                                 TALLOC_CTX *mem_ctx,
53                                                 struct tevent_context *ev,
54                                                 struct files_struct *src_fsp,
55                                                 off_t src_off,
56                                                 struct files_struct *dest_fsp,
57                                                 off_t dest_off,
58                                                 off_t num)
59 {
60         struct tevent_req *req;
61         struct btrfs_cc_state *cc_state;
62         struct btrfs_ioctl_clone_range_args cr_args;
63         struct lock_struct src_lck;
64         struct lock_struct dest_lck;
65         int ret;
66         NTSTATUS status;
67
68         req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
69         if (req == NULL) {
70                 return NULL;
71         }
72         cc_state->handle = handle;
73
74         status = vfs_stat_fsp(src_fsp);
75         if (tevent_req_nterror(req, status)) {
76                 return tevent_req_post(req, ev);
77         }
78
79         if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
80                 /* [MS-SMB2] Handling a Server-Side Data Copy Request */
81                 tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE);
82                 return tevent_req_post(req, ev);
83         }
84
85         init_strict_lock_struct(src_fsp,
86                                 src_fsp->op->global->open_persistent_id,
87                                 src_off,
88                                 num,
89                                 READ_LOCK,
90                                 &src_lck);
91         init_strict_lock_struct(dest_fsp,
92                                 dest_fsp->op->global->open_persistent_id,
93                                 dest_off,
94                                 num,
95                                 WRITE_LOCK,
96                                 &dest_lck);
97
98         if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &src_lck)) {
99                 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
100                 return tevent_req_post(req, ev);
101         }
102         if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &dest_lck)) {
103                 SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
104                 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
105                 return tevent_req_post(req, ev);
106         }
107
108         ZERO_STRUCT(cr_args);
109         cr_args.src_fd = src_fsp->fh->fd;
110         cr_args.src_offset = (uint64_t)src_off;
111         cr_args.dest_offset = (uint64_t)dest_off;
112         cr_args.src_length = (uint64_t)num;
113
114         ret = ioctl(dest_fsp->fh->fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
115         SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
116         SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
117         if (ret < 0) {
118                 /*
119                  * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
120                  * cloning. Which is 4096 by default, therefore fall back to
121                  * manual read/write on failure.
122                  */
123                 DEBUG(5, ("BTRFS_IOC_CLONE_RANGE failed: %s, length %llu, "
124                           "src fd: %lld off: %llu, dest fd: %d off: %llu\n",
125                           strerror(errno),
126                           (unsigned long long)cr_args.src_length,
127                           (long long)cr_args.src_fd,
128                           (unsigned long long)cr_args.src_offset,
129                           dest_fsp->fh->fd,
130                           (unsigned long long)cr_args.dest_offset));
131                 cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
132                                                                 cc_state, ev,
133                                                                 src_fsp,
134                                                                 src_off,
135                                                                 dest_fsp,
136                                                                 dest_off, num);
137                 if (tevent_req_nomem(cc_state->subreq, req)) {
138                         return tevent_req_post(req, ev);
139                 }
140                 /* wait for subreq completion */
141                 tevent_req_set_callback(cc_state->subreq,
142                                         btrfs_copy_chunk_done,
143                                         req);
144                 return req;
145
146         }
147
148         DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret));
149         /* BTRFS_IOC_CLONE_RANGE is all or nothing */
150         cc_state->copied = num;
151         tevent_req_done(req);
152         return tevent_req_post(req, ev);
153 }
154
155 /* only used if the request is passed through to next VFS module */
156 static void btrfs_copy_chunk_done(struct tevent_req *subreq)
157 {
158         struct tevent_req *req = tevent_req_callback_data(
159                 subreq, struct tevent_req);
160         struct btrfs_cc_state *cc_state = tevent_req_data(req,
161                                                         struct btrfs_cc_state);
162         NTSTATUS status;
163
164         status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle,
165                                               cc_state->subreq,
166                                               &cc_state->copied);
167         if (tevent_req_nterror(req, status)) {
168                 return;
169         }
170         tevent_req_done(req);
171 }
172
173 static NTSTATUS btrfs_copy_chunk_recv(struct vfs_handle_struct *handle,
174                                       struct tevent_req *req,
175                                       off_t *copied)
176 {
177         NTSTATUS status;
178         struct btrfs_cc_state *cc_state = tevent_req_data(req,
179                                                         struct btrfs_cc_state);
180
181         if (tevent_req_is_nterror(req, &status)) {
182                 DEBUG(4, ("server side copy chunk failed: %s\n",
183                           nt_errstr(status)));
184                 tevent_req_received(req);
185                 return status;
186         }
187
188         DEBUG(10, ("server side copy chunk copied %llu\n",
189                    (unsigned long long)cc_state->copied));
190         *copied = cc_state->copied;
191         tevent_req_received(req);
192         return NT_STATUS_OK;
193 }
194
195 /*
196  * caller must pass a non-null fsp or smb_fname. If fsp is null, then
197  * fall back to opening the corresponding file to issue the ioctl.
198  */
199 static NTSTATUS btrfs_get_compression(struct vfs_handle_struct *handle,
200                                       TALLOC_CTX *mem_ctx,
201                                       struct files_struct *fsp,
202                                       struct smb_filename *smb_fname,
203                                       uint16_t *_compression_fmt)
204 {
205         int ret;
206         long flags = 0;
207         int fd;
208         bool opened = false;
209         NTSTATUS status;
210
211         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
212                 fd = fsp->fh->fd;
213         } else if (smb_fname != NULL) {
214                 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
215                         DIR *dir = opendir(smb_fname->base_name);
216                         if (dir == NULL) {
217                                 return NT_STATUS_UNSUCCESSFUL;
218                         }
219                         fd = dirfd(dir);
220                 } else {
221                         fd = open(smb_fname->base_name, O_RDONLY);
222                 }
223                 if (fd < 0) {
224                         return NT_STATUS_UNSUCCESSFUL;
225                 }
226                 opened = true;
227         } else {
228                 return NT_STATUS_INVALID_PARAMETER;
229         }
230
231         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
232         if (ret < 0) {
233                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
234                           strerror(errno), (long long)fd));
235                 status = map_nt_error_from_unix(errno);
236                 goto err_close;
237         }
238         if (flags & FS_COMPR_FL) {
239                 *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
240         } else {
241                 *_compression_fmt = COMPRESSION_FORMAT_NONE;
242         }
243         status = NT_STATUS_OK;
244 err_close:
245         if (opened) {
246                 close(fd);
247         }
248
249         return status;
250 }
251
252 static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
253                                       TALLOC_CTX *mem_ctx,
254                                       struct files_struct *fsp,
255                                       uint16_t compression_fmt)
256 {
257         int ret;
258         long flags = 0;
259         int fd;
260         NTSTATUS status;
261
262         if ((fsp == NULL) || (fsp->fh->fd == -1)) {
263                 status = NT_STATUS_INVALID_PARAMETER;
264                 goto err_out;
265         }
266         fd = fsp->fh->fd;
267
268         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
269         if (ret < 0) {
270                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
271                           strerror(errno), fd));
272                 status = map_nt_error_from_unix(errno);
273                 goto err_out;
274         }
275
276         if (compression_fmt == COMPRESSION_FORMAT_NONE) {
277                 DEBUG(5, ("setting compression\n"));
278                 flags &= (~FS_COMPR_FL);
279         } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
280                 || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
281                 DEBUG(5, ("clearing compression\n"));
282                 flags |= FS_COMPR_FL;
283         } else {
284                 DEBUG(1, ("invalid compression format 0x%x\n",
285                           (int)compression_fmt));
286                 status = NT_STATUS_INVALID_PARAMETER;
287                 goto err_out;
288         }
289
290         ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
291         if (ret < 0) {
292                 DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
293                           strerror(errno), fd));
294                 status = map_nt_error_from_unix(errno);
295                 goto err_out;
296         }
297         status = NT_STATUS_OK;
298 err_out:
299         return status;
300 }
301
302
303 static struct vfs_fn_pointers btrfs_fns = {
304         .copy_chunk_send_fn = btrfs_copy_chunk_send,
305         .copy_chunk_recv_fn = btrfs_copy_chunk_recv,
306         .get_compression_fn = btrfs_get_compression,
307         .set_compression_fn = btrfs_set_compression,
308 };
309
310 NTSTATUS vfs_btrfs_init(void);
311 NTSTATUS vfs_btrfs_init(void)
312 {
313         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
314                                 "btrfs", &btrfs_fns);
315 }