LCOV - code coverage report
Current view: top level - source3/smbd - durable.c (source / functions) Hit Total Coverage
Test: coverage report for master 6248eab5 Lines: 252 431 58.5 %
Date: 2021-08-25 13:27:56 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Durable Handle default VFS implementation
       4             : 
       5             :    Copyright (C) Stefan Metzmacher 2012
       6             :    Copyright (C) Michael Adam 2012
       7             :    Copyright (C) Volker Lendecke 2012
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "system/filesys.h"
      25             : #include "lib/util/server_id.h"
      26             : #include "locking/share_mode_lock.h"
      27             : #include "smbd/smbd.h"
      28             : #include "smbd/globals.h"
      29             : #include "libcli/security/security.h"
      30             : #include "messages.h"
      31             : #include "librpc/gen_ndr/ndr_open_files.h"
      32             : #include "serverid.h"
      33             : #include "fake_file.h"
      34             : #include "locking/leases_db.h"
      35             : 
      36         674 : NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
      37             :                                     TALLOC_CTX *mem_ctx,
      38             :                                     DATA_BLOB *cookie_blob)
      39             : {
      40         674 :         struct connection_struct *conn = fsp->conn;
      41             :         enum ndr_err_code ndr_err;
      42             :         struct vfs_default_durable_cookie cookie;
      43             : 
      44         674 :         if (!lp_durable_handles(SNUM(conn))) {
      45           0 :                 return NT_STATUS_NOT_SUPPORTED;
      46             :         }
      47             : 
      48         674 :         if (lp_kernel_share_modes(SNUM(conn))) {
      49             :                 /*
      50             :                  * We do not support durable handles
      51             :                  * if kernel share modes (flocks) are used
      52             :                  */
      53          26 :                 return NT_STATUS_NOT_SUPPORTED;
      54             :         }
      55             : 
      56         648 :         if (lp_kernel_oplocks(SNUM(conn))) {
      57             :                 /*
      58             :                  * We do not support durable handles
      59             :                  * if kernel oplocks are used
      60             :                  */
      61           0 :                 return NT_STATUS_NOT_SUPPORTED;
      62             :         }
      63             : 
      64         654 :         if ((fsp->current_lock_count > 0) &&
      65           6 :             lp_posix_locking(fsp->conn->params))
      66             :         {
      67             :                 /*
      68             :                  * We do not support durable handles
      69             :                  * if the handle has posix locks.
      70             :                  */
      71           0 :                 return NT_STATUS_NOT_SUPPORTED;
      72             :         }
      73             : 
      74         648 :         if (fsp->fsp_flags.is_directory) {
      75           0 :                 return NT_STATUS_NOT_SUPPORTED;
      76             :         }
      77             : 
      78         648 :         if (fsp_get_io_fd(fsp) == -1) {
      79           0 :                 return NT_STATUS_NOT_SUPPORTED;
      80             :         }
      81             : 
      82         648 :         if (is_ntfs_stream_smb_fname(fsp->fsp_name)) {
      83             :                 /*
      84             :                  * We do not support durable handles
      85             :                  * on streams for now.
      86             :                  */
      87           0 :                 return NT_STATUS_NOT_SUPPORTED;
      88             :         }
      89             : 
      90         648 :         if (is_fake_file(fsp->fsp_name)) {
      91             :                 /*
      92             :                  * We do not support durable handles
      93             :                  * on fake files.
      94             :                  */
      95           0 :                 return NT_STATUS_NOT_SUPPORTED;
      96             :         }
      97             : 
      98         648 :         ZERO_STRUCT(cookie);
      99         648 :         cookie.allow_reconnect = false;
     100         648 :         cookie.id = fsp->file_id;
     101         648 :         cookie.servicepath = conn->connectpath;
     102         648 :         cookie.base_name = fsp->fsp_name->base_name;
     103         648 :         cookie.initial_allocation_size = fsp->initial_allocation_size;
     104         648 :         cookie.position_information = fh_get_position_information(fsp->fh);
     105         648 :         cookie.update_write_time_triggered =
     106         648 :                 fsp->fsp_flags.update_write_time_triggered;
     107         648 :         cookie.update_write_time_on_close =
     108         648 :                 fsp->fsp_flags.update_write_time_on_close;
     109         648 :         cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
     110         648 :         cookie.close_write_time = full_timespec_to_nt_time(
     111         648 :                 &fsp->close_write_time);
     112             : 
     113         648 :         cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
     114         648 :         cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
     115         648 :         cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
     116         648 :         cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
     117         648 :         cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
     118         648 :         cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
     119         648 :         cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
     120         648 :         cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
     121         648 :         cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
     122         648 :         cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
     123         648 :         cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
     124         648 :         cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
     125         648 :         cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
     126         648 :         cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
     127         648 :         cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
     128         648 :         cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
     129             : 
     130         648 :         ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
     131             :                         (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
     132         648 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     133           0 :                 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
     134           0 :                 return status;
     135             :         }
     136             : 
     137         648 :         return NT_STATUS_OK;
     138             : }
     139             : 
     140         164 : NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
     141             :                                         const DATA_BLOB old_cookie,
     142             :                                         TALLOC_CTX *mem_ctx,
     143             :                                         DATA_BLOB *new_cookie)
     144             : {
     145         164 :         struct connection_struct *conn = fsp->conn;
     146             :         NTSTATUS status;
     147             :         enum ndr_err_code ndr_err;
     148             :         struct vfs_default_durable_cookie cookie;
     149         164 :         DATA_BLOB new_cookie_blob = data_blob_null;
     150             :         struct share_mode_lock *lck;
     151             :         bool ok;
     152             : 
     153         164 :         *new_cookie = data_blob_null;
     154             : 
     155         164 :         ZERO_STRUCT(cookie);
     156             : 
     157         164 :         ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
     158             :                         (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
     159         164 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     160           0 :                 status = ndr_map_error2ntstatus(ndr_err);
     161           0 :                 return status;
     162             :         }
     163             : 
     164         164 :         if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
     165           0 :                 return NT_STATUS_INVALID_PARAMETER;
     166             :         }
     167             : 
     168         164 :         if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
     169           0 :                 return NT_STATUS_INVALID_PARAMETER;
     170             :         }
     171             : 
     172         164 :         if (!file_id_equal(&fsp->file_id, &cookie.id)) {
     173           0 :                 return NT_STATUS_INVALID_PARAMETER;
     174             :         }
     175             : 
     176         164 :         if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
     177           0 :                 return NT_STATUS_NOT_SUPPORTED;
     178             :         }
     179             : 
     180             :         /*
     181             :          * For now let it be simple and do not keep
     182             :          * delete on close files durable open
     183             :          */
     184         164 :         if (fsp->fsp_flags.initial_delete_on_close) {
     185           8 :                 return NT_STATUS_NOT_SUPPORTED;
     186             :         }
     187         156 :         if (fsp->fsp_flags.delete_on_close) {
     188           0 :                 return NT_STATUS_NOT_SUPPORTED;
     189             :         }
     190             : 
     191         156 :         if (!VALID_STAT(fsp->fsp_name->st)) {
     192           0 :                 return NT_STATUS_NOT_SUPPORTED;
     193             :         }
     194             : 
     195         156 :         if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
     196           0 :                 return NT_STATUS_NOT_SUPPORTED;
     197             :         }
     198             : 
     199             :         /* Ensure any pending write time updates are done. */
     200         156 :         if (fsp->update_write_time_event) {
     201          18 :                 fsp_flush_write_time_update(fsp);
     202             :         }
     203             : 
     204             :         /*
     205             :          * The above checks are done in mark_share_mode_disconnected() too
     206             :          * but we want to avoid getting the lock if possible
     207             :          */
     208         156 :         lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
     209         156 :         if (lck != NULL) {
     210             :                 struct smb_file_time ft;
     211             : 
     212         156 :                 init_smb_file_time(&ft);
     213             : 
     214         156 :                 if (fsp->fsp_flags.write_time_forced) {
     215           0 :                         NTTIME mtime = share_mode_changed_write_time(lck);
     216           0 :                         ft.mtime = nt_time_to_full_timespec(mtime);
     217         156 :                 } else if (fsp->fsp_flags.update_write_time_on_close) {
     218           4 :                         if (is_omit_timespec(&fsp->close_write_time)) {
     219           4 :                                 ft.mtime = timespec_current();
     220             :                         } else {
     221           0 :                                 ft.mtime = fsp->close_write_time;
     222             :                         }
     223             :                 }
     224             : 
     225         156 :                 if (!is_omit_timespec(&ft.mtime)) {
     226           4 :                         round_timespec(conn->ts_res, &ft.mtime);
     227           4 :                         file_ntimes(conn, fsp, &ft);
     228             :                 }
     229             : 
     230         156 :                 ok = mark_share_mode_disconnected(lck, fsp);
     231         156 :                 if (!ok) {
     232           0 :                         TALLOC_FREE(lck);
     233             :                 }
     234             :         }
     235         156 :         if (lck != NULL) {
     236         156 :                 ok = brl_mark_disconnected(fsp);
     237         156 :                 if (!ok) {
     238           0 :                         TALLOC_FREE(lck);
     239             :                 }
     240             :         }
     241         156 :         if (lck == NULL) {
     242           0 :                 return NT_STATUS_NOT_SUPPORTED;
     243             :         }
     244         156 :         TALLOC_FREE(lck);
     245             : 
     246         156 :         status = vfs_stat_fsp(fsp);
     247         156 :         if (!NT_STATUS_IS_OK(status)) {
     248           0 :                 return status;
     249             :         }
     250             : 
     251         156 :         ZERO_STRUCT(cookie);
     252         156 :         cookie.allow_reconnect = true;
     253         156 :         cookie.id = fsp->file_id;
     254         156 :         cookie.servicepath = conn->connectpath;
     255         156 :         cookie.base_name = fsp_str_dbg(fsp);
     256         156 :         cookie.initial_allocation_size = fsp->initial_allocation_size;
     257         156 :         cookie.position_information = fh_get_position_information(fsp->fh);
     258         156 :         cookie.update_write_time_triggered =
     259         156 :                 fsp->fsp_flags.update_write_time_triggered;
     260         156 :         cookie.update_write_time_on_close =
     261         156 :                 fsp->fsp_flags.update_write_time_on_close;
     262         156 :         cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
     263         156 :         cookie.close_write_time = full_timespec_to_nt_time(
     264         156 :                 &fsp->close_write_time);
     265             : 
     266         156 :         cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
     267         156 :         cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
     268         156 :         cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
     269         156 :         cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
     270         156 :         cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
     271         156 :         cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
     272         156 :         cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
     273         156 :         cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
     274         156 :         cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
     275         156 :         cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
     276         156 :         cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
     277         156 :         cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
     278         156 :         cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
     279         156 :         cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
     280         156 :         cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
     281         156 :         cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
     282             : 
     283         156 :         ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
     284             :                         (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
     285         156 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     286           0 :                 status = ndr_map_error2ntstatus(ndr_err);
     287           0 :                 return status;
     288             :         }
     289             : 
     290         156 :         status = fd_close(fsp);
     291         156 :         if (!NT_STATUS_IS_OK(status)) {
     292           0 :                 data_blob_free(&new_cookie_blob);
     293           0 :                 return status;
     294             :         }
     295             : 
     296         156 :         *new_cookie = new_cookie_blob;
     297         156 :         return NT_STATUS_OK;
     298             : }
     299             : 
     300             : 
     301             : /**
     302             :  * Check whether a cookie-stored struct info is the same
     303             :  * as a given SMB_STRUCT_STAT, as coming with the fsp.
     304             :  */
     305         128 : static bool vfs_default_durable_reconnect_check_stat(
     306             :                                 struct vfs_default_durable_stat *cookie_st,
     307             :                                 SMB_STRUCT_STAT *fsp_st,
     308             :                                 const char *name)
     309             : {
     310             :         int ret;
     311             : 
     312         128 :         if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
     313           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     314             :                           "stat_ex.%s differs: "
     315             :                           "cookie:%llu != stat:%llu, "
     316             :                           "denying durable reconnect\n",
     317             :                           name,
     318             :                           "st_ex_mode",
     319             :                           (unsigned long long)cookie_st->st_ex_mode,
     320             :                           (unsigned long long)fsp_st->st_ex_mode));
     321           0 :                 return false;
     322             :         }
     323             : 
     324         128 :         if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
     325           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     326             :                           "stat_ex.%s differs: "
     327             :                           "cookie:%llu != stat:%llu, "
     328             :                           "denying durable reconnect\n",
     329             :                           name,
     330             :                           "st_ex_nlink",
     331             :                           (unsigned long long)cookie_st->st_ex_nlink,
     332             :                           (unsigned long long)fsp_st->st_ex_nlink));
     333           0 :                 return false;
     334             :         }
     335             : 
     336         128 :         if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
     337           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     338             :                           "stat_ex.%s differs: "
     339             :                           "cookie:%llu != stat:%llu, "
     340             :                           "denying durable reconnect\n",
     341             :                           name,
     342             :                           "st_ex_uid",
     343             :                           (unsigned long long)cookie_st->st_ex_uid,
     344             :                           (unsigned long long)fsp_st->st_ex_uid));
     345           0 :                 return false;
     346             :         }
     347             : 
     348         128 :         if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
     349           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     350             :                           "stat_ex.%s differs: "
     351             :                           "cookie:%llu != stat:%llu, "
     352             :                           "denying durable reconnect\n",
     353             :                           name,
     354             :                           "st_ex_gid",
     355             :                           (unsigned long long)cookie_st->st_ex_gid,
     356             :                           (unsigned long long)fsp_st->st_ex_gid));
     357           0 :                 return false;
     358             :         }
     359             : 
     360         128 :         if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
     361           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     362             :                           "stat_ex.%s differs: "
     363             :                           "cookie:%llu != stat:%llu, "
     364             :                           "denying durable reconnect\n",
     365             :                           name,
     366             :                           "st_ex_rdev",
     367             :                           (unsigned long long)cookie_st->st_ex_rdev,
     368             :                           (unsigned long long)fsp_st->st_ex_rdev));
     369           0 :                 return false;
     370             :         }
     371             : 
     372         128 :         if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
     373           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     374             :                           "stat_ex.%s differs: "
     375             :                           "cookie:%llu != stat:%llu, "
     376             :                           "denying durable reconnect\n",
     377             :                           name,
     378             :                           "st_ex_size",
     379             :                           (unsigned long long)cookie_st->st_ex_size,
     380             :                           (unsigned long long)fsp_st->st_ex_size));
     381           0 :                 return false;
     382             :         }
     383             : 
     384         128 :         ret = timespec_compare(&cookie_st->st_ex_atime,
     385         128 :                                &fsp_st->st_ex_atime);
     386         128 :         if (ret != 0) {
     387             :                 struct timeval tc, ts;
     388           0 :                 tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
     389           0 :                 ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
     390             : 
     391           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     392             :                           "stat_ex.%s differs: "
     393             :                           "cookie:'%s' != stat:'%s', "
     394             :                           "denying durable reconnect\n",
     395             :                           name,
     396             :                           "st_ex_atime",
     397             :                           timeval_string(talloc_tos(), &tc, true),
     398             :                           timeval_string(talloc_tos(), &ts, true)));
     399           0 :                 return false;
     400             :         }
     401             : 
     402         128 :         ret = timespec_compare(&cookie_st->st_ex_mtime,
     403         128 :                                &fsp_st->st_ex_mtime);
     404         128 :         if (ret != 0) {
     405             :                 struct timeval tc, ts;
     406           0 :                 tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
     407           0 :                 ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
     408             : 
     409           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     410             :                           "stat_ex.%s differs: "
     411             :                           "cookie:'%s' != stat:'%s', "
     412             :                           "denying durable reconnect\n",
     413             :                           name,
     414             :                           "st_ex_mtime",
     415             :                           timeval_string(talloc_tos(), &tc, true),
     416             :                           timeval_string(talloc_tos(), &ts, true)));
     417           0 :                 return false;
     418             :         }
     419             : 
     420         128 :         ret = timespec_compare(&cookie_st->st_ex_ctime,
     421         128 :                                &fsp_st->st_ex_ctime);
     422         128 :         if (ret != 0) {
     423             :                 struct timeval tc, ts;
     424           0 :                 tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
     425           0 :                 ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
     426             : 
     427           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     428             :                           "stat_ex.%s differs: "
     429             :                           "cookie:'%s' != stat:'%s', "
     430             :                           "denying durable reconnect\n",
     431             :                           name,
     432             :                           "st_ex_ctime",
     433             :                           timeval_string(talloc_tos(), &tc, true),
     434             :                           timeval_string(talloc_tos(), &ts, true)));
     435           0 :                 return false;
     436             :         }
     437             : 
     438         128 :         ret = timespec_compare(&cookie_st->st_ex_btime,
     439         128 :                                &fsp_st->st_ex_btime);
     440         128 :         if (ret != 0) {
     441             :                 struct timeval tc, ts;
     442           0 :                 tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
     443           0 :                 ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
     444             : 
     445           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     446             :                           "stat_ex.%s differs: "
     447             :                           "cookie:'%s' != stat:'%s', "
     448             :                           "denying durable reconnect\n",
     449             :                           name,
     450             :                           "st_ex_btime",
     451             :                           timeval_string(talloc_tos(), &tc, true),
     452             :                           timeval_string(talloc_tos(), &ts, true)));
     453           0 :                 return false;
     454             :         }
     455             : 
     456         128 :         if (cookie_st->st_ex_iflags != fsp_st->st_ex_iflags) {
     457           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     458             :                           "stat_ex.%s differs: "
     459             :                           "cookie:%llu != stat:%llu, "
     460             :                           "denying durable reconnect\n",
     461             :                           name,
     462             :                           "st_ex_calculated_birthtime",
     463             :                           (unsigned long long)cookie_st->st_ex_iflags,
     464             :                           (unsigned long long)fsp_st->st_ex_iflags));
     465           0 :                 return false;
     466             :         }
     467             : 
     468         128 :         if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
     469           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     470             :                           "stat_ex.%s differs: "
     471             :                           "cookie:%llu != stat:%llu, "
     472             :                           "denying durable reconnect\n",
     473             :                           name,
     474             :                           "st_ex_blksize",
     475             :                           (unsigned long long)cookie_st->st_ex_blksize,
     476             :                           (unsigned long long)fsp_st->st_ex_blksize));
     477           0 :                 return false;
     478             :         }
     479             : 
     480         128 :         if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
     481           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     482             :                           "stat_ex.%s differs: "
     483             :                           "cookie:%llu != stat:%llu, "
     484             :                           "denying durable reconnect\n",
     485             :                           name,
     486             :                           "st_ex_blocks",
     487             :                           (unsigned long long)cookie_st->st_ex_blocks,
     488             :                           (unsigned long long)fsp_st->st_ex_blocks));
     489           0 :                 return false;
     490             :         }
     491             : 
     492         128 :         if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
     493           0 :                 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
     494             :                           "stat_ex.%s differs: "
     495             :                           "cookie:%llu != stat:%llu, "
     496             :                           "denying durable reconnect\n",
     497             :                           name,
     498             :                           "st_ex_flags",
     499             :                           (unsigned long long)cookie_st->st_ex_flags,
     500             :                           (unsigned long long)fsp_st->st_ex_flags));
     501           0 :                 return false;
     502             :         }
     503             : 
     504         128 :         return true;
     505             : }
     506             : 
     507         148 : static bool durable_reconnect_fn(
     508             :         struct share_mode_entry *e,
     509             :         bool *modified,
     510             :         void *private_data)
     511             : {
     512         148 :         struct share_mode_entry *dst_e = private_data;
     513             : 
     514         148 :         if (dst_e->pid.pid != 0) {
     515           0 :                 DBG_INFO("Found more than one entry, invalidating previous\n");
     516           0 :                 dst_e->pid.pid = 0;
     517           0 :                 return true;    /* end the loop through share mode entries */
     518             :         }
     519         148 :         *dst_e = *e;
     520         148 :         return false;           /* Look at potential other entries */
     521             : }
     522             : 
     523         156 : NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
     524             :                                        struct smb_request *smb1req,
     525             :                                        struct smbXsrv_open *op,
     526             :                                        const DATA_BLOB old_cookie,
     527             :                                        TALLOC_CTX *mem_ctx,
     528             :                                        files_struct **result,
     529             :                                        DATA_BLOB *new_cookie)
     530             : {
     531         156 :         const struct loadparm_substitution *lp_sub =
     532             :                 loadparm_s3_global_substitution();
     533             :         struct share_mode_lock *lck;
     534             :         struct share_mode_entry e;
     535         156 :         struct files_struct *fsp = NULL;
     536             :         NTSTATUS status;
     537             :         bool ok;
     538             :         int ret;
     539         156 :         int flags = 0;
     540             :         struct file_id file_id;
     541         156 :         struct smb_filename *smb_fname = NULL;
     542             :         enum ndr_err_code ndr_err;
     543             :         struct vfs_default_durable_cookie cookie;
     544         156 :         DATA_BLOB new_cookie_blob = data_blob_null;
     545             : 
     546         156 :         *result = NULL;
     547         156 :         *new_cookie = data_blob_null;
     548             : 
     549         156 :         if (!lp_durable_handles(SNUM(conn))) {
     550           0 :                 return NT_STATUS_NOT_SUPPORTED;
     551             :         }
     552             : 
     553             :         /*
     554             :          * the checks for kernel oplocks
     555             :          * and similar things are done
     556             :          * in the vfs_default_durable_cookie()
     557             :          * call below.
     558             :          */
     559             : 
     560         156 :         ndr_err = ndr_pull_struct_blob_all(
     561             :                 &old_cookie,
     562             :                 talloc_tos(),
     563             :                 &cookie,
     564             :                 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
     565         156 :         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
     566           0 :                 status = ndr_map_error2ntstatus(ndr_err);
     567           0 :                 return status;
     568             :         }
     569             : 
     570         156 :         if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
     571           0 :                 return NT_STATUS_INVALID_PARAMETER;
     572             :         }
     573             : 
     574         156 :         if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
     575           0 :                 return NT_STATUS_INVALID_PARAMETER;
     576             :         }
     577             : 
     578         156 :         if (!cookie.allow_reconnect) {
     579           8 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     580             :         }
     581             : 
     582         148 :         if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
     583           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     584             :         }
     585             : 
     586             :         /* Create an smb_filename with stream_name == NULL. */
     587         148 :         smb_fname = synthetic_smb_fname(talloc_tos(),
     588             :                                         cookie.base_name,
     589             :                                         NULL,
     590             :                                         NULL,
     591             :                                         0,
     592             :                                         0);
     593         148 :         if (smb_fname == NULL) {
     594           0 :                 return NT_STATUS_NO_MEMORY;
     595             :         }
     596             : 
     597         148 :         ret = SMB_VFS_LSTAT(conn, smb_fname);
     598         148 :         if (ret == -1) {
     599           0 :                 status = map_nt_error_from_unix_common(errno);
     600           0 :                 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
     601             :                           smb_fname_str_dbg(smb_fname),
     602             :                           nt_errstr(status)));
     603           0 :                 return status;
     604             :         }
     605             : 
     606         148 :         if (!S_ISREG(smb_fname->st.st_ex_mode)) {
     607           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     608             :         }
     609             : 
     610         148 :         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
     611         148 :         if (!file_id_equal(&cookie.id, &file_id)) {
     612           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     613             :         }
     614             : 
     615             :         /*
     616             :          * 1. check entry in locking.tdb
     617             :          */
     618             : 
     619         148 :         lck = get_existing_share_mode_lock(mem_ctx, file_id);
     620         148 :         if (lck == NULL) {
     621           0 :                 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
     622             :                            "not obtained from db\n"));
     623           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     624             :         }
     625             : 
     626         148 :         e = (struct share_mode_entry) { .pid.pid = 0 };
     627             : 
     628         148 :         ok = share_mode_forall_entries(lck, durable_reconnect_fn, &e);
     629         148 :         if (!ok) {
     630           0 :                 DBG_WARNING("share_mode_forall_entries failed\n");
     631           0 :                 TALLOC_FREE(lck);
     632           0 :                 return NT_STATUS_INTERNAL_DB_ERROR;
     633             :         }
     634             : 
     635         148 :         if (e.pid.pid == 0) {
     636           0 :                 DBG_WARNING("Did not find a unique valid share mode entry\n");
     637           0 :                 TALLOC_FREE(lck);
     638           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     639             :         }
     640             : 
     641         148 :         if (!server_id_is_disconnected(&e.pid)) {
     642          12 :                 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
     643             :                           "reconnect for handle that was not marked "
     644             :                           "disconnected (e.g. smbd or cluster node died)\n"));
     645          12 :                 TALLOC_FREE(lck);
     646          12 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     647             :         }
     648             : 
     649         136 :         if (e.share_file_id != op->global->open_persistent_id) {
     650           0 :                 DBG_INFO("denying durable "
     651             :                          "share_file_id changed %"PRIu64" != %"PRIu64" "
     652             :                          "(e.g. another client had opened the file)\n",
     653             :                          e.share_file_id,
     654             :                          op->global->open_persistent_id);
     655           0 :                 TALLOC_FREE(lck);
     656           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     657             :         }
     658             : 
     659         272 :         if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
     660         136 :             !CAN_WRITE(conn))
     661             :         {
     662           0 :                 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
     663             :                           "share[%s] is not writeable anymore\n",
     664             :                           lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
     665           0 :                 TALLOC_FREE(lck);
     666           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     667             :         }
     668             : 
     669             :         /*
     670             :          * 2. proceed with opening file
     671             :          */
     672             : 
     673         136 :         status = fsp_new(conn, conn, &fsp);
     674         136 :         if (!NT_STATUS_IS_OK(status)) {
     675           0 :                 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
     676             :                           "new fsp: %s\n", nt_errstr(status)));
     677           0 :                 TALLOC_FREE(lck);
     678           0 :                 return status;
     679             :         }
     680             : 
     681         136 :         fh_set_private_options(fsp->fh, e.private_options);
     682         136 :         fsp->file_id = file_id;
     683         136 :         fsp->file_pid = smb1req->smbpid;
     684         136 :         fsp->vuid = smb1req->vuid;
     685         136 :         fsp->open_time = e.time;
     686         136 :         fsp->access_mask = e.access_mask;
     687         136 :         fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0);
     688         136 :         fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
     689         136 :         fsp->fnum = op->local_id;
     690         136 :         fsp_set_gen_id(fsp);
     691             : 
     692             :         /*
     693             :          * TODO:
     694             :          * Do we need to store the modified flag in the DB?
     695             :          */
     696         136 :         fsp->fsp_flags.modified = false;
     697             :         /*
     698             :          * no durables for directories
     699             :          */
     700         136 :         fsp->fsp_flags.is_directory = false;
     701             :         /*
     702             :          * For normal files, can_lock == !is_directory
     703             :          */
     704         136 :         fsp->fsp_flags.can_lock = true;
     705             :         /*
     706             :          * We do not support aio write behind for smb2
     707             :          */
     708         136 :         fsp->fsp_flags.aio_write_behind = false;
     709         136 :         fsp->oplock_type = e.op_type;
     710             : 
     711         136 :         if (fsp->oplock_type == LEASE_OPLOCK) {
     712             :                 uint32_t current_state;
     713             :                 uint16_t lease_version, epoch;
     714             : 
     715             :                 /*
     716             :                  * Ensure the existing client guid matches the
     717             :                  * stored one in the share_mode_entry.
     718             :                  */
     719          58 :                 if (!GUID_equal(fsp_client_guid(fsp),
     720             :                                 &e.client_guid)) {
     721           8 :                         TALLOC_FREE(lck);
     722           8 :                         file_free(smb1req, fsp);
     723          16 :                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     724             :                 }
     725             : 
     726          50 :                 status = leases_db_get(
     727             :                         &e.client_guid,
     728             :                         &e.lease_key,
     729             :                         &file_id,
     730             :                         &current_state, /* current_state */
     731             :                         NULL, /* breaking */
     732             :                         NULL, /* breaking_to_requested */
     733             :                         NULL, /* breaking_to_required */
     734             :                         &lease_version, /* lease_version */
     735             :                         &epoch); /* epoch */
     736          50 :                 if (!NT_STATUS_IS_OK(status)) {
     737           0 :                         TALLOC_FREE(lck);
     738           0 :                         file_free(smb1req, fsp);
     739           0 :                         return status;
     740             :                 }
     741             : 
     742          50 :                 fsp->lease = find_fsp_lease(
     743             :                         fsp,
     744             :                         &e.lease_key,
     745             :                         current_state,
     746             :                         lease_version,
     747             :                         epoch);
     748          50 :                 if (fsp->lease == NULL) {
     749           0 :                         TALLOC_FREE(lck);
     750           0 :                         file_free(smb1req, fsp);
     751           0 :                         return NT_STATUS_NO_MEMORY;
     752             :                 }
     753             :         }
     754             : 
     755         128 :         fsp->initial_allocation_size = cookie.initial_allocation_size;
     756         128 :         fh_set_position_information(fsp->fh, cookie.position_information);
     757         256 :         fsp->fsp_flags.update_write_time_triggered =
     758         256 :                 cookie.update_write_time_triggered;
     759         256 :         fsp->fsp_flags.update_write_time_on_close =
     760         256 :                 cookie.update_write_time_on_close;
     761         128 :         fsp->fsp_flags.write_time_forced = cookie.write_time_forced;
     762         128 :         fsp->close_write_time = nt_time_to_full_timespec(
     763             :                 cookie.close_write_time);
     764             : 
     765         128 :         status = fsp_set_smb_fname(fsp, smb_fname);
     766         128 :         if (!NT_STATUS_IS_OK(status)) {
     767           0 :                 TALLOC_FREE(lck);
     768           0 :                 file_free(smb1req, fsp);
     769           0 :                 DEBUG(0, ("vfs_default_durable_reconnect: "
     770             :                           "fsp_set_smb_fname failed: %s\n",
     771             :                           nt_errstr(status)));
     772           0 :                 return status;
     773             :         }
     774             : 
     775         128 :         op->compat = fsp;
     776         128 :         fsp->op = op;
     777             : 
     778         256 :         ok = reset_share_mode_entry(
     779             :                 lck,
     780             :                 e.pid,
     781             :                 e.share_file_id,
     782         128 :                 messaging_server_id(conn->sconn->msg_ctx),
     783             :                 smb1req->mid,
     784         128 :                 fh_get_gen_id(fsp->fh));
     785         128 :         if (!ok) {
     786           0 :                 DBG_DEBUG("Could not set new share_mode_entry values\n");
     787           0 :                 TALLOC_FREE(lck);
     788           0 :                 op->compat = NULL;
     789           0 :                 fsp->op = NULL;
     790           0 :                 file_free(smb1req, fsp);
     791           0 :                 return NT_STATUS_INTERNAL_ERROR;
     792             :         }
     793             : 
     794         128 :         ok = brl_reconnect_disconnected(fsp);
     795         128 :         if (!ok) {
     796           0 :                 status = NT_STATUS_INTERNAL_ERROR;
     797           0 :                 DEBUG(1, ("vfs_default_durable_reconnect: "
     798             :                           "failed to reopen brlocks: %s\n",
     799             :                           nt_errstr(status)));
     800           0 :                 TALLOC_FREE(lck);
     801           0 :                 op->compat = NULL;
     802           0 :                 fsp->op = NULL;
     803           0 :                 file_free(smb1req, fsp);
     804           0 :                 return status;
     805             :         }
     806             : 
     807             :         /*
     808             :          * TODO: properly calculate open flags
     809             :          */
     810         128 :         if (fsp->fsp_flags.can_write && fsp->fsp_flags.can_read) {
     811         128 :                 flags = O_RDWR;
     812           0 :         } else if (fsp->fsp_flags.can_write) {
     813           0 :                 flags = O_WRONLY;
     814           0 :         } else if (fsp->fsp_flags.can_read) {
     815           0 :                 flags = O_RDONLY;
     816             :         }
     817             : 
     818         128 :         status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, flags, 0);
     819         128 :         if (!NT_STATUS_IS_OK(status)) {
     820           0 :                 TALLOC_FREE(lck);
     821           0 :                 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
     822             :                           "file: %s\n", nt_errstr(status)));
     823           0 :                 op->compat = NULL;
     824           0 :                 fsp->op = NULL;
     825           0 :                 file_free(smb1req, fsp);
     826           0 :                 return status;
     827             :         }
     828             : 
     829             :         /*
     830             :          * We now check the stat info stored in the cookie against
     831             :          * the current stat data from the file we just opened.
     832             :          * If any detail differs, we deny the durable reconnect,
     833             :          * because in that case it is very likely that someone
     834             :          * opened the file while the handle was disconnected,
     835             :          * which has to be interpreted as an oplock break.
     836             :          */
     837             : 
     838         128 :         ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
     839         128 :         if (ret == -1) {
     840           0 :                 status = map_nt_error_from_unix_common(errno);
     841           0 :                 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
     842             :                           smb_fname_str_dbg(smb_fname),
     843             :                           nt_errstr(status)));
     844           0 :                 ret = SMB_VFS_CLOSE(fsp);
     845           0 :                 if (ret == -1) {
     846           0 :                         DEBUG(0, ("vfs_default_durable_reconnect: "
     847             :                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
     848             :                                   "descriptor\n", strerror(errno)));
     849             :                 }
     850           0 :                 TALLOC_FREE(lck);
     851           0 :                 op->compat = NULL;
     852           0 :                 fsp->op = NULL;
     853           0 :                 file_free(smb1req, fsp);
     854           0 :                 return status;
     855             :         }
     856             : 
     857         128 :         if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
     858           0 :                 ret = SMB_VFS_CLOSE(fsp);
     859           0 :                 if (ret == -1) {
     860           0 :                         DEBUG(0, ("vfs_default_durable_reconnect: "
     861             :                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
     862             :                                   "descriptor\n", strerror(errno)));
     863             :                 }
     864           0 :                 TALLOC_FREE(lck);
     865           0 :                 op->compat = NULL;
     866           0 :                 fsp->op = NULL;
     867           0 :                 file_free(smb1req, fsp);
     868           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     869             :         }
     870             : 
     871         128 :         file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
     872         128 :         if (!file_id_equal(&cookie.id, &file_id)) {
     873           0 :                 ret = SMB_VFS_CLOSE(fsp);
     874           0 :                 if (ret == -1) {
     875           0 :                         DEBUG(0, ("vfs_default_durable_reconnect: "
     876             :                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
     877             :                                   "descriptor\n", strerror(errno)));
     878             :                 }
     879           0 :                 TALLOC_FREE(lck);
     880           0 :                 op->compat = NULL;
     881           0 :                 fsp->op = NULL;
     882           0 :                 file_free(smb1req, fsp);
     883           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     884             :         }
     885             : 
     886         128 :         (void)fdos_mode(fsp);
     887             : 
     888         256 :         ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
     889         128 :                                                       &fsp->fsp_name->st,
     890             :                                                       fsp_str_dbg(fsp));
     891         128 :         if (!ok) {
     892           0 :                 ret = SMB_VFS_CLOSE(fsp);
     893           0 :                 if (ret == -1) {
     894           0 :                         DEBUG(0, ("vfs_default_durable_reconnect: "
     895             :                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
     896             :                                   "descriptor\n", strerror(errno)));
     897             :                 }
     898           0 :                 TALLOC_FREE(lck);
     899           0 :                 op->compat = NULL;
     900           0 :                 fsp->op = NULL;
     901           0 :                 file_free(smb1req, fsp);
     902           0 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     903             :         }
     904             : 
     905         128 :         status = set_file_oplock(fsp);
     906         128 :         if (!NT_STATUS_IS_OK(status)) {
     907           0 :                 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
     908             :                           "after opening file: %s\n", nt_errstr(status)));
     909           0 :                 ret = SMB_VFS_CLOSE(fsp);
     910           0 :                 if (ret == -1) {
     911           0 :                         DEBUG(0, ("vfs_default_durable_reconnect: "
     912             :                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
     913             :                                   "descriptor\n", strerror(errno)));
     914             :                 }
     915           0 :                 TALLOC_FREE(lck);
     916           0 :                 op->compat = NULL;
     917           0 :                 fsp->op = NULL;
     918           0 :                 file_free(smb1req, fsp);
     919           0 :                 return status;
     920             :         }
     921             : 
     922         128 :         status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
     923         128 :         if (!NT_STATUS_IS_OK(status)) {
     924           0 :                 TALLOC_FREE(lck);
     925           0 :                 DEBUG(1, ("vfs_default_durable_reconnect: "
     926             :                           "vfs_default_durable_cookie - %s\n",
     927             :                           nt_errstr(status)));
     928           0 :                 op->compat = NULL;
     929           0 :                 fsp->op = NULL;
     930           0 :                 file_free(smb1req, fsp);
     931           0 :                 return status;
     932             :         }
     933             : 
     934         128 :         smb1req->chain_fsp = fsp;
     935         128 :         smb1req->smb2req->compat_chain_fsp = fsp;
     936             : 
     937         128 :         DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
     938             :                    fsp_str_dbg(fsp)));
     939             : 
     940         128 :         TALLOC_FREE(lck);
     941             : 
     942         128 :         fsp->fsp_flags.is_fsa = true;
     943             : 
     944         128 :         *result = fsp;
     945         128 :         *new_cookie = new_cookie_blob;
     946             : 
     947         128 :         return NT_STATUS_OK;
     948             : }

Generated by: LCOV version 1.13