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