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