2 Unix SMB/CIFS implementation.
3 Durable Handle default VFS implementation
5 Copyright (C) Stefan Metzmacher 2012
6 Copyright (C) Michael Adam 2012
7 Copyright (C) Volker Lendecke 2012
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.
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.
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/>.
24 #include "system/filesys.h"
25 #include "smbd/smbd.h"
26 #include "smbd/globals.h"
27 #include "libcli/security/security.h"
29 #include "librpc/gen_ndr/ndr_open_files.h"
31 #include "fake_file.h"
33 NTSTATUS
vfs_default_durable_cookie(struct files_struct
*fsp
,
35 DATA_BLOB
*cookie_blob
)
37 struct connection_struct
*conn
= fsp
->conn
;
38 enum ndr_err_code ndr_err
;
39 struct vfs_default_durable_cookie cookie
;
41 if (!lp_durable_handles(SNUM(conn
))) {
42 return NT_STATUS_NOT_SUPPORTED
;
45 if (lp_kernel_share_modes(SNUM(conn
))) {
47 * We do not support durable handles
48 * if kernel share modes (flocks) are used
50 return NT_STATUS_NOT_SUPPORTED
;
53 if (lp_kernel_oplocks(SNUM(conn
))) {
55 * We do not support durable handles
56 * if kernel oplocks are used
58 return NT_STATUS_NOT_SUPPORTED
;
61 if ((fsp
->current_lock_count
> 0) &&
62 lp_posix_locking(fsp
->conn
->params
))
65 * We do not support durable handles
66 * if the handle has posix locks.
68 return NT_STATUS_NOT_SUPPORTED
;
71 if (fsp
->is_directory
) {
72 return NT_STATUS_NOT_SUPPORTED
;
75 if (fsp
->fh
->fd
== -1) {
76 return NT_STATUS_NOT_SUPPORTED
;
79 if (is_ntfs_stream_smb_fname(fsp
->fsp_name
)) {
81 * We do not support durable handles
84 return NT_STATUS_NOT_SUPPORTED
;
87 if (is_fake_file(fsp
->fsp_name
)) {
89 * We do not support durable handles
92 return NT_STATUS_NOT_SUPPORTED
;
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
;
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
);
113 NTSTATUS
vfs_default_durable_disconnect(struct files_struct
*fsp
,
114 const DATA_BLOB old_cookie
,
116 DATA_BLOB
*new_cookie
)
118 struct connection_struct
*conn
= fsp
->conn
;
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
;
126 *new_cookie
= data_blob_null
;
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
);
137 if (strcmp(cookie
.magic
, VFS_DEFAULT_DURABLE_COOKIE_MAGIC
) != 0) {
138 return NT_STATUS_INVALID_PARAMETER
;
141 if (cookie
.version
!= VFS_DEFAULT_DURABLE_COOKIE_VERSION
) {
142 return NT_STATUS_INVALID_PARAMETER
;
145 if (!file_id_equal(&fsp
->file_id
, &cookie
.id
)) {
146 return NT_STATUS_INVALID_PARAMETER
;
149 if (!BATCH_OPLOCK_TYPE(fsp
->oplock_type
)) {
150 return NT_STATUS_NOT_SUPPORTED
;
153 if (fsp
->num_pending_break_messages
> 0) {
154 return NT_STATUS_NOT_SUPPORTED
;
158 * For now let it be simple and do not keep
159 * delete on close files durable open
161 if (fsp
->initial_delete_on_close
) {
162 return NT_STATUS_NOT_SUPPORTED
;
164 if (fsp
->delete_on_close
) {
165 return NT_STATUS_NOT_SUPPORTED
;
168 if (!VALID_STAT(fsp
->fsp_name
->st
)) {
169 return NT_STATUS_NOT_SUPPORTED
;
172 if (!S_ISREG(fsp
->fsp_name
->st
.st_ex_mode
)) {
173 return NT_STATUS_NOT_SUPPORTED
;
177 * The above checks are done in mark_share_mode_disconnected() too
178 * but we want to avoid getting the lock if possible
180 lck
= get_existing_share_mode_lock(talloc_tos(), fsp
->file_id
);
182 ok
= mark_share_mode_disconnected(lck
, fsp
);
188 ok
= brl_mark_disconnected(fsp
);
194 return NT_STATUS_NOT_SUPPORTED
;
199 cookie
.allow_reconnect
= true;
200 cookie
.id
= fsp
->file_id
;
201 cookie
.servicepath
= conn
->connectpath
;
202 cookie
.base_name
= fsp
->fsp_name
->base_name
;
203 cookie
.initial_allocation_size
= fsp
->initial_allocation_size
;
204 cookie
.position_information
= fsp
->fh
->position_information
;
206 ndr_err
= ndr_push_struct_blob(&new_cookie_blob
, mem_ctx
, &cookie
,
207 (ndr_push_flags_fn_t
)ndr_push_vfs_default_durable_cookie
);
208 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
209 status
= ndr_map_error2ntstatus(ndr_err
);
213 status
= fd_close(fsp
);
214 if (!NT_STATUS_IS_OK(status
)) {
215 data_blob_free(&new_cookie_blob
);
219 *new_cookie
= new_cookie_blob
;
223 NTSTATUS
vfs_default_durable_reconnect(struct connection_struct
*conn
,
224 struct smb_request
*smb1req
,
225 struct smbXsrv_open
*op
,
226 const DATA_BLOB old_cookie
,
228 files_struct
**result
,
229 DATA_BLOB
*new_cookie
)
231 struct share_mode_lock
*lck
;
232 struct share_mode_entry
*e
;
233 struct files_struct
*fsp
= NULL
;
238 struct file_id file_id
;
239 struct smb_filename
*smb_fname
= NULL
;
240 enum ndr_err_code ndr_err
;
241 struct vfs_default_durable_cookie cookie
;
242 DATA_BLOB new_cookie_blob
= data_blob_null
;
245 *new_cookie
= data_blob_null
;
247 if (!lp_durable_handles(SNUM(conn
))) {
248 return NT_STATUS_NOT_SUPPORTED
;
252 * the checks for kernel oplocks
253 * and similar things are done
254 * in the vfs_default_durable_cookie()
260 ndr_err
= ndr_pull_struct_blob(&old_cookie
, talloc_tos(), &cookie
,
261 (ndr_pull_flags_fn_t
)ndr_pull_vfs_default_durable_cookie
);
262 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
263 status
= ndr_map_error2ntstatus(ndr_err
);
267 if (strcmp(cookie
.magic
, VFS_DEFAULT_DURABLE_COOKIE_MAGIC
) != 0) {
268 return NT_STATUS_INVALID_PARAMETER
;
271 if (cookie
.version
!= VFS_DEFAULT_DURABLE_COOKIE_VERSION
) {
272 return NT_STATUS_INVALID_PARAMETER
;
275 if (!cookie
.allow_reconnect
) {
276 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
279 if (strcmp(cookie
.servicepath
, conn
->connectpath
) != 0) {
280 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
283 /* Create an smb_filename with stream_name == NULL. */
284 status
= create_synthetic_smb_fname(talloc_tos(),
288 if (!NT_STATUS_IS_OK(status
)) {
292 ret
= SMB_VFS_LSTAT(conn
, smb_fname
);
294 status
= map_nt_error_from_unix_common(errno
);
295 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
296 smb_fname_str_dbg(smb_fname
),
301 if (!S_ISREG(smb_fname
->st
.st_ex_mode
)) {
302 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
305 file_id
= vfs_file_id_from_sbuf(conn
, &smb_fname
->st
);
306 if (!file_id_equal(&cookie
.id
, &file_id
)) {
307 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
311 * 1. check entry in locking.tdb
314 lck
= get_existing_share_mode_lock(mem_ctx
, file_id
);
316 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
317 "not obtained from db\n"));
318 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
321 if (lck
->data
->num_share_modes
== 0) {
322 DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
323 "entry in existing share mode lock\n"));
325 return NT_STATUS_INTERNAL_DB_ERROR
;
328 if (lck
->data
->num_share_modes
> 1) {
330 * It can't be durable if there is more than one handle
333 DEBUG(5, ("vfs_default_durable_reconnect: more than one "
334 "share-mode entry - can not be durable\n"));
336 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
339 e
= &lck
->data
->share_modes
[0];
341 if (!server_id_is_disconnected(&e
->pid
)) {
342 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
343 "reconnect for handle that was not marked "
344 "disconnected (e.g. smbd or cluster node died)\n"));
346 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
349 if (e
->share_file_id
!= op
->global
->open_persistent_id
) {
350 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
351 "share_file_id changed %llu != %llu"
352 "(e.g. another client had opened the file)\n",
353 (unsigned long long)e
->share_file_id
,
354 (unsigned long long)op
->global
->open_persistent_id
));
356 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
359 if ((e
->access_mask
& (FILE_WRITE_DATA
|FILE_APPEND_DATA
)) &&
362 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
363 "share[%s] is not writeable anymore\n",
364 lp_servicename(talloc_tos(), SNUM(conn
))));
366 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
371 * add scavenger timer functionality
373 * For now we always allow the reconnect
376 expire_time
= op
->global
->disconnect_time
;
377 expire_time
+= NTTIME_MAGIC(op
->global
->durable_timeout_msec
);
379 //TODO reopen and close before telling the client...
384 * 2. proceed with opening file
387 status
= fsp_new(conn
, conn
, &fsp
);
388 if (!NT_STATUS_IS_OK(status
)) {
389 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
390 "new fsp: %s\n", nt_errstr(status
)));
395 fsp
->fh
->private_options
= e
->private_options
;
396 fsp
->fh
->gen_id
= smbXsrv_open_hash(op
);
397 fsp
->file_id
= file_id
;
398 fsp
->file_pid
= smb1req
->smbpid
;
399 fsp
->vuid
= smb1req
->vuid
;
400 fsp
->open_time
= e
->time
;
401 fsp
->access_mask
= e
->access_mask
;
402 fsp
->share_access
= e
->share_access
;
403 fsp
->can_read
= ((fsp
->access_mask
& (FILE_READ_DATA
)) != 0);
404 fsp
->can_write
= ((fsp
->access_mask
& (FILE_WRITE_DATA
|FILE_APPEND_DATA
)) != 0);
408 * Do we need to store the modified flag in the DB?
409 * How to handle update_write_time and friends
410 * during a disconnected client on a durable handle?
412 fsp
->modified
= false;
414 * no durables for directories
416 fsp
->is_directory
= false;
418 * For normal files, can_lock == !is_directory
420 fsp
->can_lock
= true;
422 * We do not support aio write behind for smb2
424 fsp
->aio_write_behind
= false;
425 fsp
->oplock_type
= e
->op_type
;
427 fsp
->initial_allocation_size
= cookie
.initial_allocation_size
;
428 fsp
->fh
->position_information
= cookie
.position_information
;
430 status
= fsp_set_smb_fname(fsp
, smb_fname
);
431 if (!NT_STATUS_IS_OK(status
)) {
434 DEBUG(0, ("vfs_default_durable_reconnect: "
435 "fsp_set_smb_fname failed: %s\n",
443 e
->pid
= messaging_server_id(conn
->sconn
->msg_ctx
);
444 e
->op_mid
= smb1req
->mid
;
445 e
->share_file_id
= fsp
->fh
->gen_id
;
447 ok
= brl_reconnect_disconnected(fsp
);
449 status
= NT_STATUS_INTERNAL_ERROR
;
450 DEBUG(1, ("vfs_default_durable_reconnect: "
451 "failed to reopen brlocks: %s\n",
460 * TODO: properly calculate open flags
462 if (fsp
->can_write
&& fsp
->can_read
) {
464 } else if (fsp
->can_write
) {
466 } else if (fsp
->can_read
) {
470 status
= fd_open(conn
, fsp
, flags
, 0 /* mode */);
471 if (!NT_STATUS_IS_OK(status
)) {
473 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
474 "file: %s\n", nt_errstr(status
)));
480 ret
= SMB_VFS_FSTAT(fsp
, &fsp
->fsp_name
->st
);
482 status
= map_nt_error_from_unix_common(errno
);
483 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
484 smb_fname_str_dbg(smb_fname
),
486 ret
= SMB_VFS_CLOSE(fsp
);
488 DEBUG(0, ("vfs_default_durable_reconnect: "
489 "SMB_VFS_CLOSE failed (%s) - leaking file "
490 "descriptor\n", strerror(errno
)));
499 if (!S_ISREG(fsp
->fsp_name
->st
.st_ex_mode
)) {
500 ret
= SMB_VFS_CLOSE(fsp
);
502 DEBUG(0, ("vfs_default_durable_reconnect: "
503 "SMB_VFS_CLOSE failed (%s) - leaking file "
504 "descriptor\n", strerror(errno
)));
509 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
512 file_id
= vfs_file_id_from_sbuf(conn
, &fsp
->fsp_name
->st
);
513 if (!file_id_equal(&cookie
.id
, &file_id
)) {
514 ret
= SMB_VFS_CLOSE(fsp
);
516 DEBUG(0, ("vfs_default_durable_reconnect: "
517 "SMB_VFS_CLOSE failed (%s) - leaking file "
518 "descriptor\n", strerror(errno
)));
523 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
526 status
= set_file_oplock(fsp
, e
->op_type
);
527 if (!NT_STATUS_IS_OK(status
)) {
528 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
529 "after opening file: %s\n", nt_errstr(status
)));
530 ret
= SMB_VFS_CLOSE(fsp
);
532 DEBUG(0, ("vfs_default_durable_reconnect: "
533 "SMB_VFS_CLOSE failed (%s) - leaking file "
534 "descriptor\n", strerror(errno
)));
542 status
= vfs_default_durable_cookie(fsp
, mem_ctx
, &new_cookie_blob
);
543 if (!NT_STATUS_IS_OK(status
)) {
545 DEBUG(1, ("vfs_default_durable_reconnect: "
546 "vfs_default_durable_cookie - %s\n",
553 smb1req
->chain_fsp
= fsp
;
554 smb1req
->smb2req
->compat_chain_fsp
= fsp
;
556 DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
560 * release the sharemode lock: this writes the changes
562 lck
->data
->modified
= true;
566 *new_cookie
= new_cookie_blob
;