s3:smbd/durable: trigger pending write_time updates before disconnecting the file
[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
103         ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
104                         (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
105         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
106                 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
107                 return status;
108         }
109
110         return NT_STATUS_OK;
111 }
112
113 NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
114                                         const DATA_BLOB old_cookie,
115                                         TALLOC_CTX *mem_ctx,
116                                         DATA_BLOB *new_cookie)
117 {
118         struct connection_struct *conn = fsp->conn;
119         NTSTATUS status;
120         enum ndr_err_code ndr_err;
121         struct vfs_default_durable_cookie cookie;
122         DATA_BLOB new_cookie_blob = data_blob_null;
123         struct share_mode_lock *lck;
124         bool ok;
125
126         *new_cookie = data_blob_null;
127
128         ZERO_STRUCT(cookie);
129
130         ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
131                         (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
132         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133                 status = ndr_map_error2ntstatus(ndr_err);
134                 return status;
135         }
136
137         if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
138                 return NT_STATUS_INVALID_PARAMETER;
139         }
140
141         if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
142                 return NT_STATUS_INVALID_PARAMETER;
143         }
144
145         if (!file_id_equal(&fsp->file_id, &cookie.id)) {
146                 return NT_STATUS_INVALID_PARAMETER;
147         }
148
149         if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
150                 return NT_STATUS_NOT_SUPPORTED;
151         }
152
153         if (fsp->num_pending_break_messages > 0) {
154                 return NT_STATUS_NOT_SUPPORTED;
155         }
156
157         /*
158          * For now let it be simple and do not keep
159          * delete on close files durable open
160          */
161         if (fsp->initial_delete_on_close) {
162                 return NT_STATUS_NOT_SUPPORTED;
163         }
164         if (fsp->delete_on_close) {
165                 return NT_STATUS_NOT_SUPPORTED;
166         }
167
168         if (!VALID_STAT(fsp->fsp_name->st)) {
169                 return NT_STATUS_NOT_SUPPORTED;
170         }
171
172         if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
173                 return NT_STATUS_NOT_SUPPORTED;
174         }
175
176         /* Ensure any pending write time updates are done. */
177         if (fsp->update_write_time_event) {
178                 update_write_time_handler(fsp->conn->sconn->ev_ctx,
179                                         fsp->update_write_time_event,
180                                         timeval_current(),
181                                         (void *)fsp);
182         }
183
184         /*
185          * The above checks are done in mark_share_mode_disconnected() too
186          * but we want to avoid getting the lock if possible
187          */
188         lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
189         if (lck != NULL) {
190                 ok = mark_share_mode_disconnected(lck, fsp);
191                 if (!ok) {
192                         TALLOC_FREE(lck);
193                 }
194         }
195         if (lck != NULL) {
196                 ok = brl_mark_disconnected(fsp);
197                 if (!ok) {
198                         TALLOC_FREE(lck);
199                 }
200         }
201         if (lck == NULL) {
202                 return NT_STATUS_NOT_SUPPORTED;
203         }
204         TALLOC_FREE(lck);
205
206         ZERO_STRUCT(cookie);
207         cookie.allow_reconnect = true;
208         cookie.id = fsp->file_id;
209         cookie.servicepath = conn->connectpath;
210         cookie.base_name = fsp->fsp_name->base_name;
211         cookie.initial_allocation_size = fsp->initial_allocation_size;
212         cookie.position_information = fsp->fh->position_information;
213
214         ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
215                         (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
216         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
217                 status = ndr_map_error2ntstatus(ndr_err);
218                 return status;
219         }
220
221         status = fd_close(fsp);
222         if (!NT_STATUS_IS_OK(status)) {
223                 data_blob_free(&new_cookie_blob);
224                 return status;
225         }
226
227         *new_cookie = new_cookie_blob;
228         return NT_STATUS_OK;
229 }
230
231 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
232                                        struct smb_request *smb1req,
233                                        struct smbXsrv_open *op,
234                                        const DATA_BLOB old_cookie,
235                                        TALLOC_CTX *mem_ctx,
236                                        files_struct **result,
237                                        DATA_BLOB *new_cookie)
238 {
239         struct share_mode_lock *lck;
240         struct share_mode_entry *e;
241         struct files_struct *fsp = NULL;
242         NTSTATUS status;
243         bool ok;
244         int ret;
245         int flags = 0;
246         struct file_id file_id;
247         struct smb_filename *smb_fname = NULL;
248         enum ndr_err_code ndr_err;
249         struct vfs_default_durable_cookie cookie;
250         DATA_BLOB new_cookie_blob = data_blob_null;
251
252         *result = NULL;
253         *new_cookie = data_blob_null;
254
255         if (!lp_durable_handles(SNUM(conn))) {
256                 return NT_STATUS_NOT_SUPPORTED;
257         }
258
259         /*
260          * the checks for kernel oplocks
261          * and similar things are done
262          * in the vfs_default_durable_cookie()
263          * call below.
264          */
265
266         ZERO_STRUCT(cookie);
267
268         ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
269                         (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
270         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
271                 status = ndr_map_error2ntstatus(ndr_err);
272                 return status;
273         }
274
275         if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
276                 return NT_STATUS_INVALID_PARAMETER;
277         }
278
279         if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
280                 return NT_STATUS_INVALID_PARAMETER;
281         }
282
283         if (!cookie.allow_reconnect) {
284                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
285         }
286
287         if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
288                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
289         }
290
291         /* Create an smb_filename with stream_name == NULL. */
292         status = create_synthetic_smb_fname(talloc_tos(),
293                                             cookie.base_name,
294                                             NULL, NULL,
295                                             &smb_fname);
296         if (!NT_STATUS_IS_OK(status)) {
297                 return status;
298         }
299
300         ret = SMB_VFS_LSTAT(conn, smb_fname);
301         if (ret == -1) {
302                 status = map_nt_error_from_unix_common(errno);
303                 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
304                           smb_fname_str_dbg(smb_fname),
305                           nt_errstr(status)));
306                 return status;
307         }
308
309         if (!S_ISREG(smb_fname->st.st_ex_mode)) {
310                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
311         }
312
313         file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
314         if (!file_id_equal(&cookie.id, &file_id)) {
315                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
316         }
317
318         /*
319          * 1. check entry in locking.tdb
320          */
321
322         lck = get_existing_share_mode_lock(mem_ctx, file_id);
323         if (lck == NULL) {
324                 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
325                            "not obtained from db\n"));
326                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
327         }
328
329         if (lck->data->num_share_modes == 0) {
330                 DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
331                           "entry in existing share mode lock\n"));
332                 TALLOC_FREE(lck);
333                 return NT_STATUS_INTERNAL_DB_ERROR;
334         }
335
336         if (lck->data->num_share_modes > 1) {
337                 /*
338                  * It can't be durable if there is more than one handle
339                  * on the file.
340                  */
341                 DEBUG(5, ("vfs_default_durable_reconnect: more than one "
342                           "share-mode entry - can not be durable\n"));
343                 TALLOC_FREE(lck);
344                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
345         }
346
347         e = &lck->data->share_modes[0];
348
349         if (!server_id_is_disconnected(&e->pid)) {
350                 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
351                           "reconnect for handle that was not marked "
352                           "disconnected (e.g. smbd or cluster node died)\n"));
353                 TALLOC_FREE(lck);
354                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
355         }
356
357         if (e->share_file_id != op->global->open_persistent_id) {
358                 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
359                           "share_file_id changed %llu != %llu"
360                           "(e.g. another client had opened the file)\n",
361                           (unsigned long long)e->share_file_id,
362                           (unsigned long long)op->global->open_persistent_id));
363                 TALLOC_FREE(lck);
364                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
365         }
366
367         if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
368             !CAN_WRITE(conn))
369         {
370                 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
371                           "share[%s] is not writeable anymore\n",
372                           lp_servicename(talloc_tos(), SNUM(conn))));
373                 TALLOC_FREE(lck);
374                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
375         }
376
377         /*
378          * TODO:
379          * add scavenger timer functionality
380          *
381          * For now we always allow the reconnect
382          */
383 #if 0
384         expire_time = op->global->disconnect_time;
385         expire_time += NTTIME_MAGIC(op->global->durable_timeout_msec);
386         if (expire < now) {
387                 //TODO reopen and close before telling the client...
388         }
389 #endif
390
391         /*
392          * 2. proceed with opening file
393          */
394
395         status = fsp_new(conn, conn, &fsp);
396         if (!NT_STATUS_IS_OK(status)) {
397                 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
398                           "new fsp: %s\n", nt_errstr(status)));
399                 TALLOC_FREE(lck);
400                 return status;
401         }
402
403         fsp->fh->private_options = e->private_options;
404         fsp->fh->gen_id = smbXsrv_open_hash(op);
405         fsp->file_id = file_id;
406         fsp->file_pid = smb1req->smbpid;
407         fsp->vuid = smb1req->vuid;
408         fsp->open_time = e->time;
409         fsp->access_mask = e->access_mask;
410         fsp->share_access = e->share_access;
411         fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0);
412         fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
413
414         /*
415          * TODO:
416          * Do we need to store the modified flag in the DB?
417          * How to handle update_write_time and friends
418          * during a disconnected client on a durable handle?
419          */
420         fsp->modified = false;
421         /*
422          * no durables for directories
423          */
424         fsp->is_directory = false;
425         /*
426          * For normal files, can_lock == !is_directory
427          */
428         fsp->can_lock = true;
429         /*
430          * We do not support aio write behind for smb2
431          */
432         fsp->aio_write_behind = false;
433         fsp->oplock_type = e->op_type;
434
435         fsp->initial_allocation_size = cookie.initial_allocation_size;
436         fsp->fh->position_information = cookie.position_information;
437
438         status = fsp_set_smb_fname(fsp, smb_fname);
439         if (!NT_STATUS_IS_OK(status)) {
440                 TALLOC_FREE(lck);
441                 fsp_free(fsp);
442                 DEBUG(0, ("vfs_default_durable_reconnect: "
443                           "fsp_set_smb_fname failed: %s\n",
444                           nt_errstr(status)));
445                 return status;
446         }
447
448         op->compat = fsp;
449         fsp->op = op;
450
451         e->pid = messaging_server_id(conn->sconn->msg_ctx);
452         e->op_mid = smb1req->mid;
453         e->share_file_id = fsp->fh->gen_id;
454
455         ok = brl_reconnect_disconnected(fsp);
456         if (!ok) {
457                 status = NT_STATUS_INTERNAL_ERROR;
458                 DEBUG(1, ("vfs_default_durable_reconnect: "
459                           "failed to reopen brlocks: %s\n",
460                           nt_errstr(status)));
461                 TALLOC_FREE(lck);
462                 op->compat = NULL;
463                 fsp_free(fsp);
464                 return status;
465         }
466
467         /*
468          * TODO: properly calculate open flags
469          */
470         if (fsp->can_write && fsp->can_read) {
471                 flags = O_RDWR;
472         } else if (fsp->can_write) {
473                 flags = O_WRONLY;
474         } else if (fsp->can_read) {
475                 flags = O_RDONLY;
476         }
477
478         status = fd_open(conn, fsp, flags, 0 /* mode */);
479         if (!NT_STATUS_IS_OK(status)) {
480                 TALLOC_FREE(lck);
481                 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
482                           "file: %s\n", nt_errstr(status)));
483                 op->compat = NULL;
484                 fsp_free(fsp);
485                 return status;
486         }
487
488         ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
489         if (ret == -1) {
490                 status = map_nt_error_from_unix_common(errno);
491                 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
492                           smb_fname_str_dbg(smb_fname),
493                           nt_errstr(status)));
494                 ret = SMB_VFS_CLOSE(fsp);
495                 if (ret == -1) {
496                         DEBUG(0, ("vfs_default_durable_reconnect: "
497                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
498                                   "descriptor\n", strerror(errno)));
499                 }
500                 TALLOC_FREE(lck);
501                 op->compat = NULL;
502                 fsp_free(fsp);
503                 return status;
504
505         }
506
507         if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
508                 ret = SMB_VFS_CLOSE(fsp);
509                 if (ret == -1) {
510                         DEBUG(0, ("vfs_default_durable_reconnect: "
511                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
512                                   "descriptor\n", strerror(errno)));
513                 }
514                 TALLOC_FREE(lck);
515                 op->compat = NULL;
516                 fsp_free(fsp);
517                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
518         }
519
520         file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
521         if (!file_id_equal(&cookie.id, &file_id)) {
522                 ret = SMB_VFS_CLOSE(fsp);
523                 if (ret == -1) {
524                         DEBUG(0, ("vfs_default_durable_reconnect: "
525                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
526                                   "descriptor\n", strerror(errno)));
527                 }
528                 TALLOC_FREE(lck);
529                 op->compat = NULL;
530                 fsp_free(fsp);
531                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
532         }
533
534         status = set_file_oplock(fsp, e->op_type);
535         if (!NT_STATUS_IS_OK(status)) {
536                 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
537                           "after opening file: %s\n", nt_errstr(status)));
538                 ret = SMB_VFS_CLOSE(fsp);
539                 if (ret == -1) {
540                         DEBUG(0, ("vfs_default_durable_reconnect: "
541                                   "SMB_VFS_CLOSE failed (%s) - leaking file "
542                                   "descriptor\n", strerror(errno)));
543                 }
544                 TALLOC_FREE(lck);
545                 op->compat = NULL;
546                 fsp_free(fsp);
547                 return status;
548         }
549
550         status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
551         if (!NT_STATUS_IS_OK(status)) {
552                 TALLOC_FREE(lck);
553                 DEBUG(1, ("vfs_default_durable_reconnect: "
554                           "vfs_default_durable_cookie - %s\n",
555                           nt_errstr(status)));
556                 op->compat = NULL;
557                 fsp_free(fsp);
558                 return status;
559         }
560
561         smb1req->chain_fsp = fsp;
562         smb1req->smb2req->compat_chain_fsp = fsp;
563
564         DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
565                    fsp_str_dbg(fsp)));
566
567         /*
568          * release the sharemode lock: this writes the changes
569          */
570         lck->data->modified = true;
571         TALLOC_FREE(lck);
572
573         *result = fsp;
574         *new_cookie = new_cookie_blob;
575
576         return NT_STATUS_OK;
577 }