LCOV - code coverage report
Current view: top level - source3/modules - vfs_btrfs.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 2 381 0.5 %
Date: 2024-04-21 15:09:00 Functions: 1 15 6.7 %

          Line data    Source code
       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 "includes.h"
      21             : #include "system/filesys.h"
      22             : #include <linux/ioctl.h>
      23             : #include <linux/fs.h>
      24             : #include <sys/ioctl.h>
      25             : #include <unistd.h>
      26             : #include <fcntl.h>
      27             : #include <dirent.h>
      28             : #include <libgen.h>
      29             : #include "smbd/smbd.h"
      30             : #include "smbd/globals.h"
      31             : #include "librpc/gen_ndr/smbXsrv.h"
      32             : #include "librpc/gen_ndr/ioctl.h"
      33             : #include "lib/util/tevent_ntstatus.h"
      34             : #include "offload_token.h"
      35             : 
      36           0 : static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
      37             :                                       enum timestamp_set_resolution *_ts_res)
      38             : {
      39             :         uint32_t fs_capabilities;
      40             :         enum timestamp_set_resolution ts_res;
      41             : 
      42             :         /* inherit default capabilities, expose compression support */
      43           0 :         fs_capabilities = SMB_VFS_NEXT_FS_CAPABILITIES(handle, &ts_res);
      44           0 :         fs_capabilities |= (FILE_FILE_COMPRESSION
      45             :                             | FILE_SUPPORTS_BLOCK_REFCOUNTING);
      46           0 :         *_ts_res = ts_res;
      47             : 
      48           0 :         return fs_capabilities;
      49             : }
      50             : 
      51             : #define SHADOW_COPY_PREFIX "@GMT-"    /* vfs_shadow_copy format */
      52             : #define SHADOW_COPY_PATH_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
      53             : 
      54             : #define BTRFS_SUBVOL_RDONLY             (1ULL << 1)
      55             : #define BTRFS_SUBVOL_NAME_MAX           4039
      56             : #define BTRFS_PATH_NAME_MAX             4087
      57             : struct btrfs_ioctl_vol_args_v2 {
      58             :         int64_t fd;
      59             :         uint64_t transid;
      60             :         uint64_t flags;
      61             :         uint64_t unused[4];
      62             :         char name[BTRFS_SUBVOL_NAME_MAX + 1];
      63             : };
      64             : struct btrfs_ioctl_vol_args {
      65             :         int64_t fd;
      66             :         char name[BTRFS_PATH_NAME_MAX + 1];
      67             : };
      68             : 
      69             : struct btrfs_ioctl_clone_range_args {
      70             :         int64_t src_fd;
      71             :         uint64_t src_offset;
      72             :         uint64_t src_length;
      73             :         uint64_t dest_offset;
      74             : };
      75             : 
      76             : #define BTRFS_IOCTL_MAGIC 0x94
      77             : #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
      78             :                                    struct btrfs_ioctl_clone_range_args)
      79             : #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
      80             :                                     struct btrfs_ioctl_vol_args)
      81             : #define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
      82             :                                       struct btrfs_ioctl_vol_args_v2)
      83             : 
      84             : static struct vfs_offload_ctx *btrfs_offload_ctx;
      85             : 
      86             : struct btrfs_offload_read_state {
      87             :         struct vfs_handle_struct *handle;
      88             :         files_struct *fsp;
      89             :         uint32_t flags;
      90             :         uint64_t xferlen;
      91             :         DATA_BLOB token;
      92             : };
      93             : 
      94             : static void btrfs_offload_read_done(struct tevent_req *subreq);
      95             : 
      96           0 : static struct tevent_req *btrfs_offload_read_send(
      97             :         TALLOC_CTX *mem_ctx,
      98             :         struct tevent_context *ev,
      99             :         struct vfs_handle_struct *handle,
     100             :         files_struct *fsp,
     101             :         uint32_t fsctl,
     102             :         uint32_t ttl,
     103             :         off_t offset,
     104             :         size_t to_copy)
     105             : {
     106           0 :         struct tevent_req *req = NULL;
     107           0 :         struct tevent_req *subreq = NULL;
     108           0 :         struct btrfs_offload_read_state *state = NULL;
     109             :         NTSTATUS status;
     110             : 
     111           0 :         req = tevent_req_create(mem_ctx, &state,
     112             :                                 struct btrfs_offload_read_state);
     113           0 :         if (req == NULL) {
     114           0 :                 return NULL;
     115             :         }
     116           0 :         *state = (struct btrfs_offload_read_state) {
     117             :                 .handle = handle,
     118             :                 .fsp = fsp,
     119             :         };
     120             : 
     121           0 :         status = vfs_offload_token_ctx_init(fsp->conn->sconn->client,
     122             :                                             &btrfs_offload_ctx);
     123           0 :         if (tevent_req_nterror(req, status)) {
     124           0 :                 return tevent_req_post(req, ev);
     125             :         }
     126             : 
     127           0 :         if (fsctl == FSCTL_DUP_EXTENTS_TO_FILE) {
     128           0 :                 status = vfs_offload_token_create_blob(state, fsp, fsctl,
     129           0 :                                                        &state->token);
     130           0 :                 if (tevent_req_nterror(req, status)) {
     131           0 :                         return tevent_req_post(req, ev);
     132             :                 }
     133             : 
     134           0 :                 status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx, fsp,
     135           0 :                                                         &state->token);
     136           0 :                 if (tevent_req_nterror(req, status)) {
     137           0 :                         return tevent_req_post(req, ev);
     138             :                 }
     139           0 :                 tevent_req_done(req);
     140           0 :                 return tevent_req_post(req, ev);
     141             :         }
     142             : 
     143           0 :         subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
     144             :                                                 fsctl, ttl, offset, to_copy);
     145           0 :         if (tevent_req_nomem(subreq, req)) {
     146           0 :                 return tevent_req_post(req, ev);
     147             :         }
     148           0 :         tevent_req_set_callback(subreq, btrfs_offload_read_done, req);
     149           0 :         return req;
     150             : }
     151             : 
     152           0 : static void btrfs_offload_read_done(struct tevent_req *subreq)
     153             : {
     154           0 :         struct tevent_req *req = tevent_req_callback_data(
     155             :                 subreq, struct tevent_req);
     156           0 :         struct btrfs_offload_read_state *state = tevent_req_data(
     157             :                 req, struct btrfs_offload_read_state);
     158             :         NTSTATUS status;
     159             : 
     160           0 :         status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
     161             :                                                 state->handle,
     162             :                                                 state,
     163             :                                                 &state->flags,
     164             :                                                 &state->xferlen,
     165             :                                                 &state->token);
     166           0 :         TALLOC_FREE(subreq);
     167           0 :         if (tevent_req_nterror(req, status)) {
     168           0 :                 return;
     169             :         }
     170             : 
     171           0 :         status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx,
     172           0 :                                                 state->fsp,
     173           0 :                                                 &state->token);
     174           0 :         if (tevent_req_nterror(req, status)) {
     175           0 :                 return;
     176             :         }
     177             : 
     178           0 :         tevent_req_done(req);
     179           0 :         return;
     180             : }
     181             : 
     182           0 : static NTSTATUS btrfs_offload_read_recv(struct tevent_req *req,
     183             :                                         struct vfs_handle_struct *handle,
     184             :                                         TALLOC_CTX *mem_ctx,
     185             :                                         uint32_t *flags,
     186             :                                         uint64_t *xferlen,
     187             :                                         DATA_BLOB *token)
     188             : {
     189           0 :         struct btrfs_offload_read_state *state = tevent_req_data(
     190             :                 req, struct btrfs_offload_read_state);
     191             :         NTSTATUS status;
     192             : 
     193           0 :         if (tevent_req_is_nterror(req, &status)) {
     194           0 :                 tevent_req_received(req);
     195           0 :                 return status;
     196             :         }
     197             : 
     198           0 :         *flags = state->flags;
     199           0 :         *xferlen = state->xferlen;
     200           0 :         token->length = state->token.length;
     201           0 :         token->data = talloc_move(mem_ctx, &state->token.data);
     202             : 
     203           0 :         tevent_req_received(req);
     204           0 :         return NT_STATUS_OK;
     205             : }
     206             : 
     207             : struct btrfs_offload_write_state {
     208             :         struct vfs_handle_struct *handle;
     209             :         off_t copied;
     210             :         bool need_unbecome_user;
     211             : };
     212             : 
     213           0 : static void btrfs_offload_write_cleanup(struct tevent_req *req,
     214             :                                         enum tevent_req_state req_state)
     215             : {
     216             :         struct btrfs_offload_write_state *state =
     217           0 :                 tevent_req_data(req,
     218             :                 struct btrfs_offload_write_state);
     219             :         bool ok;
     220             : 
     221           0 :         if (!state->need_unbecome_user) {
     222           0 :                 return;
     223             :         }
     224             : 
     225           0 :         ok = unbecome_user_without_service();
     226           0 :         SMB_ASSERT(ok);
     227           0 :         state->need_unbecome_user = false;
     228             : }
     229             : 
     230             : static void btrfs_offload_write_done(struct tevent_req *subreq);
     231             : 
     232           0 : static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
     233             :                                                 TALLOC_CTX *mem_ctx,
     234             :                                                 struct tevent_context *ev,
     235             :                                                 uint32_t fsctl,
     236             :                                                 DATA_BLOB *token,
     237             :                                                 off_t transfer_offset,
     238             :                                                 struct files_struct *dest_fsp,
     239             :                                                 off_t dest_off,
     240             :                                                 off_t num)
     241             : {
     242           0 :         struct tevent_req *req = NULL;
     243           0 :         struct btrfs_offload_write_state *state = NULL;
     244           0 :         struct tevent_req *subreq = NULL;
     245             :         struct btrfs_ioctl_clone_range_args cr_args;
     246             :         struct lock_struct src_lck;
     247             :         struct lock_struct dest_lck;
     248           0 :         off_t src_off = transfer_offset;
     249           0 :         files_struct *src_fsp = NULL;
     250             :         int ret;
     251           0 :         bool handle_offload_write = true;
     252           0 :         bool do_locking = false;
     253             :         NTSTATUS status;
     254             :         bool ok;
     255             : 
     256           0 :         req = tevent_req_create(mem_ctx, &state,
     257             :                                 struct btrfs_offload_write_state);
     258           0 :         if (req == NULL) {
     259           0 :                 return NULL;
     260             :         }
     261             : 
     262           0 :         state->handle = handle;
     263             : 
     264           0 :         tevent_req_set_cleanup_fn(req, btrfs_offload_write_cleanup);
     265             : 
     266           0 :         status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
     267             :                                                 token, &src_fsp);
     268           0 :         if (tevent_req_nterror(req, status)) {
     269           0 :                 return tevent_req_post(req, ev);
     270             :         }
     271             : 
     272           0 :         switch (fsctl) {
     273           0 :         case FSCTL_SRV_COPYCHUNK:
     274             :         case FSCTL_SRV_COPYCHUNK_WRITE:
     275           0 :                 do_locking = true;
     276           0 :                 break;
     277             : 
     278           0 :         case FSCTL_DUP_EXTENTS_TO_FILE:
     279             :                 /* dup extents does not use locking */
     280           0 :                 break;
     281             : 
     282           0 :         default:
     283           0 :                 handle_offload_write = false;
     284           0 :                 break;
     285             :         }
     286             : 
     287           0 :         if (num == 0) {
     288             :                 /*
     289             :                  * With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
     290             :                  * all data from @src_offset->EOF! This is certainly not what
     291             :                  * the caller expects, and not what vfs_default does.
     292             :                  */
     293           0 :                 handle_offload_write = false;
     294             :         }
     295             : 
     296           0 :         if (!handle_offload_write) {
     297           0 :                 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
     298             :                                                          state,
     299             :                                                          ev,
     300             :                                                          fsctl,
     301             :                                                          token,
     302             :                                                          transfer_offset,
     303             :                                                          dest_fsp,
     304             :                                                          dest_off,
     305             :                                                          num);
     306           0 :                 if (tevent_req_nomem(subreq, req)) {
     307           0 :                         return tevent_req_post(req, ev);
     308             :                 }
     309           0 :                 tevent_req_set_callback(subreq,
     310             :                                         btrfs_offload_write_done,
     311             :                                         req);
     312           0 :                 return req;
     313             :         }
     314             : 
     315           0 :         status = vfs_offload_token_check_handles(
     316             :                 fsctl, src_fsp, dest_fsp);
     317           0 :         if (!NT_STATUS_IS_OK(status)) {
     318           0 :                 tevent_req_nterror(req, status);
     319           0 :                 return tevent_req_post(req, ev);
     320             :         }
     321             : 
     322           0 :         ok = become_user_without_service_by_fsp(src_fsp);
     323           0 :         if (!ok) {
     324           0 :                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
     325           0 :                 return tevent_req_post(req, ev);
     326             :         }
     327           0 :         state->need_unbecome_user = true;
     328             : 
     329           0 :         status = vfs_stat_fsp(src_fsp);
     330           0 :         if (tevent_req_nterror(req, status)) {
     331           0 :                 return tevent_req_post(req, ev);
     332             :         }
     333             : 
     334           0 :         if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
     335             :                 /* [MS-SMB2] Handling a Server-Side Data Copy Request */
     336           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE);
     337           0 :                 return tevent_req_post(req, ev);
     338             :         }
     339             : 
     340           0 :         if (do_locking) {
     341           0 :                 init_strict_lock_struct(src_fsp,
     342           0 :                                         src_fsp->op->global->open_persistent_id,
     343             :                                         src_off,
     344             :                                         num,
     345             :                                         READ_LOCK,
     346             :                                         lp_posix_cifsu_locktype(src_fsp),
     347             :                                         &src_lck);
     348           0 :                 if (!SMB_VFS_STRICT_LOCK_CHECK(src_fsp->conn, src_fsp, &src_lck)) {
     349           0 :                         tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
     350           0 :                         return tevent_req_post(req, ev);
     351             :                 }
     352             :         }
     353             : 
     354           0 :         ok = unbecome_user_without_service();
     355           0 :         SMB_ASSERT(ok);
     356           0 :         state->need_unbecome_user = false;
     357             : 
     358           0 :         if (do_locking) {
     359           0 :                 init_strict_lock_struct(dest_fsp,
     360           0 :                                         dest_fsp->op->global->open_persistent_id,
     361             :                                         dest_off,
     362             :                                         num,
     363             :                                         WRITE_LOCK,
     364             :                                         lp_posix_cifsu_locktype(dest_fsp),
     365             :                                         &dest_lck);
     366             : 
     367           0 :                 if (!SMB_VFS_STRICT_LOCK_CHECK(dest_fsp->conn, dest_fsp, &dest_lck)) {
     368           0 :                         tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
     369           0 :                         return tevent_req_post(req, ev);
     370             :                 }
     371             :         }
     372             : 
     373           0 :         ZERO_STRUCT(cr_args);
     374           0 :         cr_args.src_fd = fsp_get_io_fd(src_fsp);
     375           0 :         cr_args.src_offset = (uint64_t)src_off;
     376           0 :         cr_args.dest_offset = (uint64_t)dest_off;
     377           0 :         cr_args.src_length = (uint64_t)num;
     378             : 
     379           0 :         ret = ioctl(fsp_get_io_fd(dest_fsp), BTRFS_IOC_CLONE_RANGE, &cr_args);
     380           0 :         if (ret < 0) {
     381             :                 /*
     382             :                  * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
     383             :                  * cloning. Which is 4096 by default, therefore fall back to
     384             :                  * manual read/write on failure.
     385             :                  */
     386           0 :                 DEBUG(5, ("BTRFS_IOC_CLONE_RANGE failed: %s, length %llu, "
     387             :                           "src fd: %lld off: %llu, dest fd: %d off: %llu\n",
     388             :                           strerror(errno),
     389             :                           (unsigned long long)cr_args.src_length,
     390             :                           (long long)cr_args.src_fd,
     391             :                           (unsigned long long)cr_args.src_offset,
     392             :                           fsp_get_io_fd(dest_fsp),
     393             :                           (unsigned long long)cr_args.dest_offset));
     394           0 :                 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
     395             :                                                          state,
     396             :                                                          ev,
     397             :                                                          fsctl,
     398             :                                                          token,
     399             :                                                          transfer_offset,
     400             :                                                          dest_fsp,
     401             :                                                          dest_off,
     402             :                                                          num);
     403           0 :                 if (tevent_req_nomem(subreq, req)) {
     404           0 :                         return tevent_req_post(req, ev);
     405             :                 }
     406             :                 /* wait for subreq completion */
     407           0 :                 tevent_req_set_callback(subreq,
     408             :                                         btrfs_offload_write_done,
     409             :                                         req);
     410           0 :                 return req;
     411             :         }
     412             : 
     413           0 :         DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret));
     414             :         /* BTRFS_IOC_CLONE_RANGE is all or nothing */
     415           0 :         state->copied = num;
     416           0 :         tevent_req_done(req);
     417           0 :         return tevent_req_post(req, ev);
     418             : }
     419             : 
     420             : /* only used if the request is passed through to next VFS module */
     421           0 : static void btrfs_offload_write_done(struct tevent_req *subreq)
     422             : {
     423             :         struct tevent_req *req =
     424           0 :                 tevent_req_callback_data(subreq,
     425             :                 struct tevent_req);
     426             :         struct btrfs_offload_write_state *state =
     427           0 :                 tevent_req_data(req,
     428             :                 struct btrfs_offload_write_state);
     429             :         NTSTATUS status;
     430             : 
     431           0 :         status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
     432             :                                                  subreq,
     433             :                                                  &state->copied);
     434           0 :         TALLOC_FREE(subreq);
     435           0 :         if (tevent_req_nterror(req, status)) {
     436           0 :                 return;
     437             :         }
     438           0 :         tevent_req_done(req);
     439             : }
     440             : 
     441           0 : static NTSTATUS btrfs_offload_write_recv(struct vfs_handle_struct *handle,
     442             :                                          struct tevent_req *req,
     443             :                                          off_t *copied)
     444             : {
     445             :         struct btrfs_offload_write_state *state =
     446           0 :                 tevent_req_data(req,
     447             :                 struct btrfs_offload_write_state);
     448             :         NTSTATUS status;
     449             : 
     450           0 :         if (tevent_req_is_nterror(req, &status)) {
     451           0 :                 DEBUG(4, ("server side copy chunk failed: %s\n",
     452             :                           nt_errstr(status)));
     453           0 :                 tevent_req_received(req);
     454           0 :                 return status;
     455             :         }
     456             : 
     457           0 :         DEBUG(10, ("server side copy chunk copied %llu\n",
     458             :                    (unsigned long long)state->copied));
     459           0 :         *copied = state->copied;
     460           0 :         tevent_req_received(req);
     461           0 :         return NT_STATUS_OK;
     462             : }
     463             : 
     464           0 : static NTSTATUS btrfs_fget_compression(struct vfs_handle_struct *handle,
     465             :                                        TALLOC_CTX *mem_ctx,
     466             :                                        struct files_struct *fsp,
     467             :                                        uint16_t *_compression_fmt)
     468             : {
     469             :         struct sys_proc_fd_path_buf buf;
     470             :         int ret;
     471           0 :         long flags = 0;
     472           0 :         int fsp_fd = fsp_get_pathref_fd(fsp);
     473           0 :         int fd = -1;
     474             :         NTSTATUS status;
     475             : 
     476           0 :         if (!fsp->fsp_flags.is_pathref) {
     477           0 :                 ret = ioctl(fsp_fd, FS_IOC_GETFLAGS, &flags);
     478           0 :                 if (ret < 0) {
     479           0 :                         DBG_WARNING("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
     480             :                                     strerror(errno), (long long)fd);
     481           0 :                         return map_nt_error_from_unix(errno);
     482             :                 }
     483           0 :                 if (flags & FS_COMPR_FL) {
     484           0 :                         *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
     485             :                 } else {
     486           0 :                         *_compression_fmt = COMPRESSION_FORMAT_NONE;
     487             :                 }
     488           0 :                 return NT_STATUS_OK;
     489             :         }
     490             : 
     491           0 :         if (!fsp->fsp_flags.have_proc_fds) {
     492           0 :                 return NT_STATUS_NOT_IMPLEMENTED;
     493             :         }
     494             : 
     495           0 :         fd = open(sys_proc_fd_path(fsp_fd, &buf), O_RDONLY);
     496           0 :         if (fd == -1) {
     497           0 :                 DBG_DEBUG("/proc open of %s failed: %s\n",
     498             :                           buf.buf,
     499             :                           strerror(errno));
     500           0 :                 return map_nt_error_from_unix(errno);
     501             :         }
     502             : 
     503           0 :         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
     504           0 :         if (ret < 0) {
     505           0 :                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
     506             :                           strerror(errno), (long long)fd));
     507           0 :                 status = map_nt_error_from_unix(errno);
     508           0 :                 goto err_close;
     509             :         }
     510           0 :         if (flags & FS_COMPR_FL) {
     511           0 :                 *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
     512             :         } else {
     513           0 :                 *_compression_fmt = COMPRESSION_FORMAT_NONE;
     514             :         }
     515           0 :         status = NT_STATUS_OK;
     516             : 
     517           0 : err_close:
     518           0 :         if (fd != -1) {
     519           0 :                 close(fd);
     520             :         }
     521             : 
     522           0 :         return status;
     523             : }
     524             : 
     525           0 : static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
     526             :                                       TALLOC_CTX *mem_ctx,
     527             :                                       struct files_struct *fsp,
     528             :                                       uint16_t compression_fmt)
     529             : {
     530             :         int ret;
     531           0 :         long flags = 0;
     532             :         int fd;
     533             :         NTSTATUS status;
     534             : 
     535           0 :         if ((fsp == NULL) || (fsp_get_io_fd(fsp) == -1)) {
     536           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     537           0 :                 goto err_out;
     538             :         }
     539           0 :         fd = fsp_get_io_fd(fsp);
     540             : 
     541           0 :         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
     542           0 :         if (ret < 0) {
     543           0 :                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
     544             :                           strerror(errno), fd));
     545           0 :                 status = map_nt_error_from_unix(errno);
     546           0 :                 goto err_out;
     547             :         }
     548             : 
     549           0 :         if (compression_fmt == COMPRESSION_FORMAT_NONE) {
     550           0 :                 DEBUG(5, ("setting compression\n"));
     551           0 :                 flags &= (~FS_COMPR_FL);
     552           0 :         } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
     553           0 :                 || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
     554           0 :                 DEBUG(5, ("clearing compression\n"));
     555           0 :                 flags |= FS_COMPR_FL;
     556             :         } else {
     557           0 :                 DEBUG(1, ("invalid compression format 0x%x\n",
     558             :                           (int)compression_fmt));
     559           0 :                 status = NT_STATUS_INVALID_PARAMETER;
     560           0 :                 goto err_out;
     561             :         }
     562             : 
     563           0 :         ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
     564           0 :         if (ret < 0) {
     565           0 :                 DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
     566             :                           strerror(errno), fd));
     567           0 :                 status = map_nt_error_from_unix(errno);
     568           0 :                 goto err_out;
     569             :         }
     570           0 :         status = NT_STATUS_OK;
     571           0 : err_out:
     572           0 :         return status;
     573             : }
     574             : 
     575             : /*
     576             :  * Check whether a path can be shadow copied. Return the base volume, allowing
     577             :  * the caller to determine if multiple paths lie on the same base volume.
     578             :  */
     579             : #define BTRFS_INODE_SUBVOL 256
     580           0 : static NTSTATUS btrfs_snap_check_path(struct vfs_handle_struct *handle,
     581             :                                       TALLOC_CTX *mem_ctx,
     582             :                                       const char *service_path,
     583             :                                       char **base_volume)
     584             : {
     585             :         struct stat st;
     586             :         char *base;
     587             : 
     588           0 :         if (!lp_parm_bool(SNUM(handle->conn),
     589             :                          "btrfs", "manipulate snapshots", false)) {
     590           0 :                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
     591           0 :                 return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle, mem_ctx,
     592             :                                                     service_path, base_volume);
     593             :         }
     594             : 
     595             :         /* btrfs userspace uses this logic to confirm subvolume */
     596           0 :         if (stat(service_path, &st) < 0) {
     597           0 :                 return NT_STATUS_NOT_SUPPORTED;
     598             :         }
     599           0 :         if ((st.st_ino != BTRFS_INODE_SUBVOL) || !S_ISDIR(st.st_mode)) {
     600           0 :                 DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
     601             :                           service_path));
     602           0 :                 return NT_STATUS_NOT_SUPPORTED;
     603             :         }
     604             : 
     605             :         /* we "snapshot" the service path itself */
     606           0 :         base = talloc_strdup(mem_ctx, service_path);
     607           0 :         if (base == NULL) {
     608           0 :                 return NT_STATUS_NO_MEMORY;
     609             :         }
     610           0 :         *base_volume = base;
     611             : 
     612           0 :         return NT_STATUS_OK;
     613             : }
     614             : 
     615           0 : static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
     616             :                                          const char *src_path,
     617             :                                          time_t *tstamp,
     618             :                                          char **dest_path, char **subvolume)
     619             : {
     620             :         struct tm t_gmt;
     621             :         char time_str[50];
     622             :         size_t tlen;
     623             : 
     624           0 :         gmtime_r(tstamp, &t_gmt);
     625             : 
     626           0 :         tlen = strftime(time_str, ARRAY_SIZE(time_str),
     627             :                         SHADOW_COPY_PATH_FORMAT, &t_gmt);
     628           0 :         if (tlen <= 0) {
     629           0 :                 return NT_STATUS_UNSUCCESSFUL;
     630             :         }
     631             : 
     632           0 :         *dest_path = talloc_strdup(mem_ctx, src_path);
     633           0 :         *subvolume = talloc_strdup(mem_ctx, time_str);
     634           0 :         if ((*dest_path == NULL) || (*subvolume == NULL)) {
     635           0 :                 return NT_STATUS_NO_MEMORY;
     636             :         }
     637             : 
     638           0 :         return NT_STATUS_OK;
     639             : }
     640             : 
     641           0 : static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
     642             :                                   TALLOC_CTX *mem_ctx,
     643             :                                   const char *base_volume,
     644             :                                   time_t *tstamp,
     645             :                                   bool rw,
     646             :                                   char **_base_path,
     647             :                                   char **_snap_path)
     648             : {
     649             :         struct btrfs_ioctl_vol_args_v2 ioctl_arg;
     650             :         DIR *src_dir;
     651             :         DIR *dest_dir;
     652             :         int src_fd;
     653             :         int dest_fd;
     654           0 :         char *dest_path = NULL;
     655           0 :         char *dest_subvolume = NULL;
     656             :         int ret;
     657             :         NTSTATUS status;
     658             :         char *base_path;
     659             :         char *snap_path;
     660             :         TALLOC_CTX *tmp_ctx;
     661             :         int saved_errno;
     662             :         size_t len;
     663             : 
     664           0 :         if (!lp_parm_bool(SNUM(handle->conn),
     665             :                           "btrfs", "manipulate snapshots", false)) {
     666           0 :                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
     667           0 :                 return SMB_VFS_NEXT_SNAP_CREATE(handle, mem_ctx, base_volume,
     668             :                                                 tstamp, rw, _base_path,
     669             :                                                 _snap_path);
     670             :         }
     671             : 
     672           0 :         tmp_ctx = talloc_new(mem_ctx);
     673           0 :         if (tmp_ctx == NULL) {
     674           0 :                 return NT_STATUS_NO_MEMORY;
     675             :         }
     676             : 
     677           0 :         base_path = talloc_strdup(tmp_ctx, base_volume);
     678           0 :         if (base_path == NULL) {
     679           0 :                 talloc_free(tmp_ctx);
     680           0 :                 return NT_STATUS_NO_MEMORY;
     681             :         }
     682             : 
     683           0 :         status = btrfs_gen_snap_dest_path(tmp_ctx, base_volume, tstamp,
     684             :                                           &dest_path, &dest_subvolume);
     685           0 :         if (!NT_STATUS_IS_OK(status)) {
     686           0 :                 talloc_free(tmp_ctx);
     687           0 :                 return status;
     688             :         }
     689             : 
     690           0 :         snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
     691             :                                     dest_subvolume);
     692           0 :         if (snap_path == NULL) {
     693           0 :                 talloc_free(tmp_ctx);
     694           0 :                 return NT_STATUS_NO_MEMORY;
     695             :         }
     696             : 
     697           0 :         src_dir = opendir(base_volume);
     698           0 :         if (src_dir == NULL) {
     699           0 :                 DEBUG(0, ("snap src %s open failed: %s\n",
     700             :                           base_volume, strerror(errno)));
     701           0 :                 status = map_nt_error_from_unix(errno);
     702           0 :                 talloc_free(tmp_ctx);
     703           0 :                 return status;
     704             :         }
     705           0 :         src_fd = dirfd(src_dir);
     706           0 :         if (src_fd < 0) {
     707           0 :                 status = map_nt_error_from_unix(errno);
     708           0 :                 closedir(src_dir);
     709           0 :                 talloc_free(tmp_ctx);
     710           0 :                 return status;
     711             :         }
     712             : 
     713           0 :         dest_dir = opendir(dest_path);
     714           0 :         if (dest_dir == NULL) {
     715           0 :                 DEBUG(0, ("snap dest %s open failed: %s\n",
     716             :                           dest_path, strerror(errno)));
     717           0 :                 status = map_nt_error_from_unix(errno);
     718           0 :                 closedir(src_dir);
     719           0 :                 talloc_free(tmp_ctx);
     720           0 :                 return status;
     721             :         }
     722           0 :         dest_fd = dirfd(dest_dir);
     723           0 :         if (dest_fd < 0) {
     724           0 :                 status = map_nt_error_from_unix(errno);
     725           0 :                 closedir(src_dir);
     726           0 :                 closedir(dest_dir);
     727           0 :                 talloc_free(tmp_ctx);
     728           0 :                 return status;
     729             :         }
     730             : 
     731             :         /* avoid zeroing the entire struct here, name is 4k */
     732           0 :         ioctl_arg.fd = src_fd;
     733           0 :         ioctl_arg.transid = 0;
     734           0 :         ioctl_arg.flags = (rw == false) ? BTRFS_SUBVOL_RDONLY : 0;
     735           0 :         memset(ioctl_arg.unused, 0, sizeof(ioctl_arg.unused));
     736           0 :         len = strlcpy(ioctl_arg.name, dest_subvolume,
     737             :                       ARRAY_SIZE(ioctl_arg.name));
     738           0 :         if (len >= ARRAY_SIZE(ioctl_arg.name)) {
     739           0 :                 DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
     740           0 :                 closedir(src_dir);
     741           0 :                 closedir(dest_dir);
     742           0 :                 talloc_free(tmp_ctx);
     743           0 :                 return NT_STATUS_INVALID_PARAMETER;
     744             :         }
     745             : 
     746           0 :         become_root();
     747           0 :         ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
     748           0 :         saved_errno = errno;
     749           0 :         unbecome_root();
     750           0 :         if (ret < 0) {
     751           0 :                 DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
     752             :                           base_volume, dest_path, dest_subvolume,
     753             :                           strerror(saved_errno)));
     754           0 :                 status = map_nt_error_from_unix(saved_errno);
     755           0 :                 closedir(src_dir);
     756           0 :                 closedir(dest_dir);
     757           0 :                 talloc_free(tmp_ctx);
     758           0 :                 return status;
     759             :         }
     760           0 :         DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
     761             :                   base_volume, dest_path, dest_subvolume));
     762             : 
     763           0 :         *_base_path = talloc_steal(mem_ctx, base_path);
     764           0 :         *_snap_path = talloc_steal(mem_ctx, snap_path);
     765           0 :         closedir(src_dir);
     766           0 :         closedir(dest_dir);
     767           0 :         talloc_free(tmp_ctx);
     768             : 
     769           0 :         return NT_STATUS_OK;
     770             : }
     771             : 
     772           0 : static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
     773             :                                   TALLOC_CTX *mem_ctx,
     774             :                                   char *base_path,
     775             :                                   char *snap_path)
     776             : {
     777             :         char *tstr;
     778           0 :         struct tm t_gmt = {};
     779             :         DIR *dest_dir;
     780             :         int dest_fd;
     781             :         struct btrfs_ioctl_vol_args ioctl_arg;
     782             :         int ret;
     783             :         NTSTATUS status;
     784             :         char *dest_path;
     785             :         char *subvolume;
     786             :         TALLOC_CTX *tmp_ctx;
     787             :         int saved_errno;
     788             :         size_t len;
     789             : 
     790           0 :         if (!lp_parm_bool(SNUM(handle->conn),
     791             :                           "btrfs", "manipulate snapshots", false)) {
     792           0 :                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
     793           0 :                 return SMB_VFS_NEXT_SNAP_DELETE(handle, mem_ctx,
     794             :                                                 base_path, snap_path);
     795             :         }
     796             : 
     797           0 :         tmp_ctx = talloc_new(mem_ctx);
     798           0 :         if (tmp_ctx == NULL) {
     799           0 :                 return NT_STATUS_NO_MEMORY;
     800             :         }
     801             : 
     802           0 :         dest_path = talloc_strdup(tmp_ctx, snap_path);
     803           0 :         if (dest_path == NULL) {
     804           0 :                 talloc_free(tmp_ctx);
     805           0 :                 return NT_STATUS_NO_MEMORY;
     806             :         }
     807           0 :         subvolume = talloc_strdup(tmp_ctx, snap_path);
     808           0 :         if (subvolume == NULL) {
     809           0 :                 talloc_free(tmp_ctx);
     810           0 :                 return NT_STATUS_NO_MEMORY;
     811             :         }
     812           0 :         dest_path = dirname(dest_path);
     813           0 :         subvolume = basename(subvolume);
     814             : 
     815             :         /* confirm snap_path matches creation format */
     816           0 :         tstr = strptime(subvolume, SHADOW_COPY_PATH_FORMAT, &t_gmt);
     817           0 :         if ((tstr == NULL) || (*tstr != '\0')) {
     818           0 :                 DEBUG(0, ("snapshot path %s does not match creation format\n",
     819             :                           snap_path));
     820           0 :                 talloc_free(tmp_ctx);
     821           0 :                 return NT_STATUS_UNSUCCESSFUL;
     822             :         }
     823             : 
     824           0 :         dest_dir = opendir(dest_path);
     825           0 :         if (dest_dir == NULL) {
     826           0 :                 DEBUG(0, ("snap destroy dest %s open failed: %s\n",
     827             :                           dest_path, strerror(errno)));
     828           0 :                 status = map_nt_error_from_unix(errno);
     829           0 :                 talloc_free(tmp_ctx);
     830           0 :                 return status;
     831             :         }
     832           0 :         dest_fd = dirfd(dest_dir);
     833           0 :         if (dest_fd < 0) {
     834           0 :                 status = map_nt_error_from_unix(errno);
     835           0 :                 closedir(dest_dir);
     836           0 :                 talloc_free(tmp_ctx);
     837           0 :                 return status;
     838             :         }
     839             : 
     840           0 :         ioctl_arg.fd = -1;      /* not needed */
     841           0 :         len = strlcpy(ioctl_arg.name, subvolume, ARRAY_SIZE(ioctl_arg.name));
     842           0 :         if (len >= ARRAY_SIZE(ioctl_arg.name)) {
     843           0 :                 DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
     844           0 :                 closedir(dest_dir);
     845           0 :                 talloc_free(tmp_ctx);
     846           0 :                 return NT_STATUS_INVALID_PARAMETER;
     847             :         }
     848             : 
     849           0 :         become_root();
     850           0 :         ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
     851           0 :         saved_errno = errno;
     852           0 :         unbecome_root();
     853           0 :         if (ret < 0) {
     854           0 :                 DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
     855             :                           dest_path, subvolume, strerror(saved_errno)));
     856           0 :                 status = map_nt_error_from_unix(saved_errno);
     857           0 :                 closedir(dest_dir);
     858           0 :                 talloc_free(tmp_ctx);
     859           0 :                 return status;
     860             :         }
     861           0 :         DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
     862             :                   dest_path, subvolume));
     863             : 
     864           0 :         closedir(dest_dir);
     865           0 :         talloc_free(tmp_ctx);
     866           0 :         return NT_STATUS_OK;
     867             : }
     868             : 
     869             : static struct vfs_fn_pointers btrfs_fns = {
     870             :         .fs_capabilities_fn = btrfs_fs_capabilities,
     871             :         .offload_read_send_fn = btrfs_offload_read_send,
     872             :         .offload_read_recv_fn = btrfs_offload_read_recv,
     873             :         .offload_write_send_fn = btrfs_offload_write_send,
     874             :         .offload_write_recv_fn = btrfs_offload_write_recv,
     875             :         .fget_compression_fn = btrfs_fget_compression,
     876             :         .set_compression_fn = btrfs_set_compression,
     877             :         .snap_check_path_fn = btrfs_snap_check_path,
     878             :         .snap_create_fn = btrfs_snap_create,
     879             :         .snap_delete_fn = btrfs_snap_delete,
     880             : };
     881             : 
     882             : static_decl_vfs;
     883          27 : NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
     884             : {
     885          27 :         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
     886             :                                 "btrfs", &btrfs_fns);
     887             : }

Generated by: LCOV version 1.14