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