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