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

Generated by: LCOV version 1.13