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