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