a6ff97e3a766593f784e1a112727a26627d26649
[herb/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 "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(
713                         fsp,
714                         &key,
715                         l->current_state,
716                         l->lease_version,
717                         l->epoch);
718                 if (fsp->lease == NULL) {
719                         TALLOC_FREE(lck);
720                         fsp_free(fsp);
721                         return NT_STATUS_NO_MEMORY;
722                 }
723
724                 /*
725                  * Ensure the existing client guid matches the
726                  * stored one in the share_mode_entry.
727                  */
728                 if (!GUID_equal(fsp_client_guid(fsp),
729                                 &e->client_guid)) {
730                         TALLOC_FREE(lck);
731                         fsp_free(fsp);
732                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
733                 }
734         }
735
736         fsp->initial_allocation_size = cookie.initial_allocation_size;
737         fsp->fh->position_information = cookie.position_information;
738         fsp->update_write_time_triggered = cookie.update_write_time_triggered;
739         fsp->update_write_time_on_close = cookie.update_write_time_on_close;
740         fsp->write_time_forced = cookie.write_time_forced;
741         fsp->close_write_time = cookie.close_write_time;
742
743         status = fsp_set_smb_fname(fsp, smb_fname);
744         if (!NT_STATUS_IS_OK(status)) {
745                 TALLOC_FREE(lck);
746                 fsp_free(fsp);
747                 DEBUG(0, ("vfs_default_durable_reconnect: "
748                           "fsp_set_smb_fname failed: %s\n",
749                           nt_errstr(status)));
750                 return status;
751         }
752
753         op->compat = fsp;
754         fsp->op = op;
755
756         e->pid = messaging_server_id(conn->sconn->msg_ctx);
757         e->op_mid = smb1req->mid;
758         e->share_file_id = fsp->fh->gen_id;
759
760         ok = brl_reconnect_disconnected(fsp);
761         if (!ok) {
762                 status = NT_STATUS_INTERNAL_ERROR;
763                 DEBUG(1, ("vfs_default_durable_reconnect: "
764                           "failed to reopen brlocks: %s\n",
765                           nt_errstr(status)));
766                 TALLOC_FREE(lck);
767                 op->compat = NULL;
768                 fsp_free(fsp);
769                 return status;
770         }
771
772         /*
773          * TODO: properly calculate open flags
774          */
775         if (fsp->can_write && fsp->can_read) {
776                 flags = O_RDWR;
777         } else if (fsp->can_write) {
778                 flags = O_WRONLY;
779         } else if (fsp->can_read) {
780                 flags = O_RDONLY;
781         }
782
783         status = fd_open(conn, fsp, flags, 0 /* mode */);
784         if (!NT_STATUS_IS_OK(status)) {
785                 TALLOC_FREE(lck);
786                 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
787                           "file: %s\n", nt_errstr(status)));
788                 op->compat = NULL;
789                 fsp_free(fsp);
790                 return status;
791         }
792
793         /*
794          * We now check the stat info stored in the cookie against
795          * the current stat data from the file we just opened.
796          * If any detail differs, we deny the durable reconnect,
797          * because in that case it is very likely that someone
798          * opened the file while the handle was disconnected,
799          * which has to be interpreted as an oplock break.
800          */
801
802         ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
803         if (ret == -1) {
804                 status = map_nt_error_from_unix_common(errno);
805                 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
806                           smb_fname_str_dbg(smb_fname),
807                           nt_errstr(status)));
808                 ret = SMB_VFS_CLOSE(fsp);
809                 if (ret == -1) {
810                         DEBUG(0, ("vfs_default_durable_reconnect: "
811                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
812                                   "descriptor\n", strerror(errno)));
813                 }
814                 TALLOC_FREE(lck);
815                 op->compat = NULL;
816                 fsp_free(fsp);
817                 return status;
818         }
819
820         if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
821                 ret = SMB_VFS_CLOSE(fsp);
822                 if (ret == -1) {
823                         DEBUG(0, ("vfs_default_durable_reconnect: "
824                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
825                                   "descriptor\n", strerror(errno)));
826                 }
827                 TALLOC_FREE(lck);
828                 op->compat = NULL;
829                 fsp_free(fsp);
830                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
831         }
832
833         file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
834         if (!file_id_equal(&cookie.id, &file_id)) {
835                 ret = SMB_VFS_CLOSE(fsp);
836                 if (ret == -1) {
837                         DEBUG(0, ("vfs_default_durable_reconnect: "
838                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
839                                   "descriptor\n", strerror(errno)));
840                 }
841                 TALLOC_FREE(lck);
842                 op->compat = NULL;
843                 fsp_free(fsp);
844                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
845         }
846
847         ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
848                                                       &fsp->fsp_name->st,
849                                                       fsp_str_dbg(fsp));
850         if (!ok) {
851                 ret = SMB_VFS_CLOSE(fsp);
852                 if (ret == -1) {
853                         DEBUG(0, ("vfs_default_durable_reconnect: "
854                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
855                                   "descriptor\n", strerror(errno)));
856                 }
857                 TALLOC_FREE(lck);
858                 op->compat = NULL;
859                 fsp_free(fsp);
860                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
861         }
862
863         status = set_file_oplock(fsp);
864         if (!NT_STATUS_IS_OK(status)) {
865                 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
866                           "after opening file: %s\n", nt_errstr(status)));
867                 ret = SMB_VFS_CLOSE(fsp);
868                 if (ret == -1) {
869                         DEBUG(0, ("vfs_default_durable_reconnect: "
870                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
871                                   "descriptor\n", strerror(errno)));
872                 }
873                 TALLOC_FREE(lck);
874                 op->compat = NULL;
875                 fsp_free(fsp);
876                 return status;
877         }
878
879         status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
880         if (!NT_STATUS_IS_OK(status)) {
881                 TALLOC_FREE(lck);
882                 DEBUG(1, ("vfs_default_durable_reconnect: "
883                           "vfs_default_durable_cookie - %s\n",
884                           nt_errstr(status)));
885                 op->compat = NULL;
886                 fsp_free(fsp);
887                 return status;
888         }
889
890         smb1req->chain_fsp = fsp;
891         smb1req->smb2req->compat_chain_fsp = fsp;
892
893         DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
894                    fsp_str_dbg(fsp)));
895
896         /*
897          * release the sharemode lock: this writes the changes
898          */
899         lck->data->modified = true;
900         TALLOC_FREE(lck);
901
902         *result = fsp;
903         *new_cookie = new_cookie_blob;
904
905         return NT_STATUS_OK;
906 }