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

Generated by: LCOV version 1.14