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