Line data Source code
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 "lib/util/server_id.h"
26 : #include "locking/share_mode_lock.h"
27 : #include "smbd/smbd.h"
28 : #include "smbd/globals.h"
29 : #include "libcli/security/security.h"
30 : #include "messages.h"
31 : #include "librpc/gen_ndr/ndr_open_files.h"
32 : #include "serverid.h"
33 : #include "fake_file.h"
34 : #include "locking/leases_db.h"
35 :
36 0 : NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
37 : TALLOC_CTX *mem_ctx,
38 : DATA_BLOB *cookie_blob)
39 : {
40 0 : struct connection_struct *conn = fsp->conn;
41 : enum ndr_err_code ndr_err;
42 : struct vfs_default_durable_cookie cookie;
43 :
44 0 : if (!lp_durable_handles(SNUM(conn))) {
45 0 : return NT_STATUS_NOT_SUPPORTED;
46 : }
47 :
48 0 : if (lp_kernel_share_modes(SNUM(conn))) {
49 : /*
50 : * We do not support durable handles
51 : * if file system sharemodes are used
52 : */
53 0 : return NT_STATUS_NOT_SUPPORTED;
54 : }
55 :
56 0 : if (lp_kernel_oplocks(SNUM(conn))) {
57 : /*
58 : * We do not support durable handles
59 : * if kernel oplocks are used
60 : */
61 0 : return NT_STATUS_NOT_SUPPORTED;
62 : }
63 :
64 0 : if ((fsp->current_lock_count > 0) &&
65 0 : lp_posix_locking(fsp->conn->params))
66 : {
67 : /*
68 : * We do not support durable handles
69 : * if the handle has posix locks.
70 : */
71 0 : return NT_STATUS_NOT_SUPPORTED;
72 : }
73 :
74 0 : if (fsp->fsp_flags.is_directory) {
75 0 : return NT_STATUS_NOT_SUPPORTED;
76 : }
77 :
78 0 : if (fsp_is_alternate_stream(fsp)) {
79 : /*
80 : * We do not support durable handles
81 : * on streams for now.
82 : */
83 0 : return NT_STATUS_NOT_SUPPORTED;
84 : }
85 :
86 0 : if (is_fake_file(fsp->fsp_name)) {
87 : /*
88 : * We do not support durable handles
89 : * on fake files.
90 : */
91 0 : return NT_STATUS_NOT_SUPPORTED;
92 : }
93 :
94 0 : ZERO_STRUCT(cookie);
95 0 : cookie.allow_reconnect = false;
96 0 : cookie.id = fsp->file_id;
97 0 : cookie.servicepath = conn->connectpath;
98 0 : cookie.base_name = fsp->fsp_name->base_name;
99 0 : cookie.initial_allocation_size = fsp->initial_allocation_size;
100 0 : cookie.position_information = fh_get_position_information(fsp->fh);
101 0 : cookie.update_write_time_triggered =
102 0 : fsp->fsp_flags.update_write_time_triggered;
103 0 : cookie.update_write_time_on_close =
104 0 : fsp->fsp_flags.update_write_time_on_close;
105 0 : cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
106 0 : cookie.close_write_time = full_timespec_to_nt_time(
107 0 : &fsp->close_write_time);
108 :
109 0 : cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
110 0 : cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
111 0 : cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
112 0 : cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
113 0 : cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
114 0 : cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
115 0 : cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
116 0 : cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
117 0 : cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
118 0 : cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
119 0 : cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
120 0 : cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
121 0 : cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
122 0 : cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
123 0 : cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
124 0 : cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
125 :
126 0 : ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
127 : (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
128 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
129 0 : NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
130 0 : return status;
131 : }
132 :
133 0 : return NT_STATUS_OK;
134 : }
135 :
136 0 : 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 0 : struct connection_struct *conn = fsp->conn;
142 : NTSTATUS status;
143 : enum ndr_err_code ndr_err;
144 : struct vfs_default_durable_cookie cookie;
145 0 : DATA_BLOB new_cookie_blob = data_blob_null;
146 : struct share_mode_lock *lck;
147 : bool ok;
148 :
149 0 : *new_cookie = data_blob_null;
150 :
151 0 : ZERO_STRUCT(cookie);
152 :
153 0 : ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
154 : (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
155 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
156 0 : status = ndr_map_error2ntstatus(ndr_err);
157 0 : return status;
158 : }
159 :
160 0 : if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
161 0 : return NT_STATUS_INVALID_PARAMETER;
162 : }
163 :
164 0 : if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
165 0 : return NT_STATUS_INVALID_PARAMETER;
166 : }
167 :
168 0 : if (!file_id_equal(&fsp->file_id, &cookie.id)) {
169 0 : return NT_STATUS_INVALID_PARAMETER;
170 : }
171 :
172 0 : if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
173 0 : 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 0 : if (fsp->fsp_flags.initial_delete_on_close) {
181 0 : return NT_STATUS_NOT_SUPPORTED;
182 : }
183 0 : if (fsp->fsp_flags.delete_on_close) {
184 0 : return NT_STATUS_NOT_SUPPORTED;
185 : }
186 :
187 0 : if (!VALID_STAT(fsp->fsp_name->st)) {
188 0 : return NT_STATUS_NOT_SUPPORTED;
189 : }
190 :
191 0 : if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
192 0 : return NT_STATUS_NOT_SUPPORTED;
193 : }
194 :
195 : /* Ensure any pending write time updates are done. */
196 0 : if (fsp->update_write_time_event) {
197 0 : fsp_flush_write_time_update(fsp);
198 : }
199 :
200 : /*
201 : * The above checks are done in mark_share_mode_disconnected() too
202 : * but we want to avoid getting the lock if possible
203 : */
204 0 : lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
205 0 : if (lck != NULL) {
206 : struct smb_file_time ft;
207 :
208 0 : init_smb_file_time(&ft);
209 :
210 0 : if (fsp->fsp_flags.write_time_forced) {
211 0 : NTTIME mtime = share_mode_changed_write_time(lck);
212 0 : ft.mtime = nt_time_to_full_timespec(mtime);
213 0 : } else if (fsp->fsp_flags.update_write_time_on_close) {
214 0 : if (is_omit_timespec(&fsp->close_write_time)) {
215 0 : ft.mtime = timespec_current();
216 : } else {
217 0 : ft.mtime = fsp->close_write_time;
218 : }
219 : }
220 :
221 0 : if (!is_omit_timespec(&ft.mtime)) {
222 0 : round_timespec(conn->ts_res, &ft.mtime);
223 0 : file_ntimes(conn, fsp, &ft);
224 : }
225 :
226 0 : ok = mark_share_mode_disconnected(lck, fsp);
227 0 : if (!ok) {
228 0 : TALLOC_FREE(lck);
229 : }
230 : }
231 0 : if (lck != NULL) {
232 0 : ok = brl_mark_disconnected(fsp);
233 0 : if (!ok) {
234 0 : TALLOC_FREE(lck);
235 : }
236 : }
237 0 : if (lck == NULL) {
238 0 : return NT_STATUS_NOT_SUPPORTED;
239 : }
240 0 : TALLOC_FREE(lck);
241 :
242 0 : status = vfs_stat_fsp(fsp);
243 0 : if (!NT_STATUS_IS_OK(status)) {
244 0 : return status;
245 : }
246 :
247 0 : ZERO_STRUCT(cookie);
248 0 : cookie.allow_reconnect = true;
249 0 : cookie.id = fsp->file_id;
250 0 : cookie.servicepath = conn->connectpath;
251 0 : cookie.base_name = fsp_str_dbg(fsp);
252 0 : cookie.initial_allocation_size = fsp->initial_allocation_size;
253 0 : cookie.position_information = fh_get_position_information(fsp->fh);
254 0 : cookie.update_write_time_triggered =
255 0 : fsp->fsp_flags.update_write_time_triggered;
256 0 : cookie.update_write_time_on_close =
257 0 : fsp->fsp_flags.update_write_time_on_close;
258 0 : cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
259 0 : cookie.close_write_time = full_timespec_to_nt_time(
260 0 : &fsp->close_write_time);
261 :
262 0 : cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
263 0 : cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
264 0 : cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
265 0 : cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
266 0 : cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
267 0 : cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
268 0 : cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
269 0 : cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
270 0 : cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
271 0 : cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
272 0 : cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
273 0 : cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
274 0 : cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
275 0 : cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
276 0 : cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
277 0 : cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
278 :
279 0 : ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
280 : (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
281 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
282 0 : status = ndr_map_error2ntstatus(ndr_err);
283 0 : return status;
284 : }
285 :
286 0 : status = fd_close(fsp);
287 0 : if (!NT_STATUS_IS_OK(status)) {
288 0 : data_blob_free(&new_cookie_blob);
289 0 : return status;
290 : }
291 :
292 0 : *new_cookie = new_cookie_blob;
293 0 : return NT_STATUS_OK;
294 : }
295 :
296 :
297 : /**
298 : * Check whether a cookie-stored struct info is the same
299 : * as a given SMB_STRUCT_STAT, as coming with the fsp.
300 : */
301 0 : static bool vfs_default_durable_reconnect_check_stat(
302 : struct vfs_default_durable_stat *cookie_st,
303 : SMB_STRUCT_STAT *fsp_st,
304 : const char *name)
305 : {
306 : int ret;
307 :
308 0 : if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
309 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
310 : "stat_ex.%s differs: "
311 : "cookie:%llu != stat:%llu, "
312 : "denying durable reconnect\n",
313 : name,
314 : "st_ex_mode",
315 : (unsigned long long)cookie_st->st_ex_mode,
316 : (unsigned long long)fsp_st->st_ex_mode));
317 0 : return false;
318 : }
319 :
320 0 : if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
321 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
322 : "stat_ex.%s differs: "
323 : "cookie:%llu != stat:%llu, "
324 : "denying durable reconnect\n",
325 : name,
326 : "st_ex_nlink",
327 : (unsigned long long)cookie_st->st_ex_nlink,
328 : (unsigned long long)fsp_st->st_ex_nlink));
329 0 : return false;
330 : }
331 :
332 0 : if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
333 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
334 : "stat_ex.%s differs: "
335 : "cookie:%llu != stat:%llu, "
336 : "denying durable reconnect\n",
337 : name,
338 : "st_ex_uid",
339 : (unsigned long long)cookie_st->st_ex_uid,
340 : (unsigned long long)fsp_st->st_ex_uid));
341 0 : return false;
342 : }
343 :
344 0 : if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
345 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
346 : "stat_ex.%s differs: "
347 : "cookie:%llu != stat:%llu, "
348 : "denying durable reconnect\n",
349 : name,
350 : "st_ex_gid",
351 : (unsigned long long)cookie_st->st_ex_gid,
352 : (unsigned long long)fsp_st->st_ex_gid));
353 0 : return false;
354 : }
355 :
356 0 : if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
357 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
358 : "stat_ex.%s differs: "
359 : "cookie:%llu != stat:%llu, "
360 : "denying durable reconnect\n",
361 : name,
362 : "st_ex_rdev",
363 : (unsigned long long)cookie_st->st_ex_rdev,
364 : (unsigned long long)fsp_st->st_ex_rdev));
365 0 : return false;
366 : }
367 :
368 0 : if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
369 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
370 : "stat_ex.%s differs: "
371 : "cookie:%llu != stat:%llu, "
372 : "denying durable reconnect\n",
373 : name,
374 : "st_ex_size",
375 : (unsigned long long)cookie_st->st_ex_size,
376 : (unsigned long long)fsp_st->st_ex_size));
377 0 : return false;
378 : }
379 :
380 0 : ret = timespec_compare(&cookie_st->st_ex_atime,
381 0 : &fsp_st->st_ex_atime);
382 0 : if (ret != 0) {
383 : struct timeval tc, ts;
384 0 : tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
385 0 : ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
386 :
387 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
388 : "stat_ex.%s differs: "
389 : "cookie:'%s' != stat:'%s', "
390 : "denying durable reconnect\n",
391 : name,
392 : "st_ex_atime",
393 : timeval_string(talloc_tos(), &tc, true),
394 : timeval_string(talloc_tos(), &ts, true)));
395 0 : return false;
396 : }
397 :
398 0 : ret = timespec_compare(&cookie_st->st_ex_mtime,
399 0 : &fsp_st->st_ex_mtime);
400 0 : if (ret != 0) {
401 : struct timeval tc, ts;
402 0 : tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
403 0 : ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
404 :
405 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
406 : "stat_ex.%s differs: "
407 : "cookie:'%s' != stat:'%s', "
408 : "denying durable reconnect\n",
409 : name,
410 : "st_ex_mtime",
411 : timeval_string(talloc_tos(), &tc, true),
412 : timeval_string(talloc_tos(), &ts, true)));
413 0 : return false;
414 : }
415 :
416 0 : ret = timespec_compare(&cookie_st->st_ex_ctime,
417 0 : &fsp_st->st_ex_ctime);
418 0 : if (ret != 0) {
419 : struct timeval tc, ts;
420 0 : tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
421 0 : ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
422 :
423 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
424 : "stat_ex.%s differs: "
425 : "cookie:'%s' != stat:'%s', "
426 : "denying durable reconnect\n",
427 : name,
428 : "st_ex_ctime",
429 : timeval_string(talloc_tos(), &tc, true),
430 : timeval_string(talloc_tos(), &ts, true)));
431 0 : return false;
432 : }
433 :
434 0 : ret = timespec_compare(&cookie_st->st_ex_btime,
435 0 : &fsp_st->st_ex_btime);
436 0 : if (ret != 0) {
437 : struct timeval tc, ts;
438 0 : tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
439 0 : ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
440 :
441 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
442 : "stat_ex.%s differs: "
443 : "cookie:'%s' != stat:'%s', "
444 : "denying durable reconnect\n",
445 : name,
446 : "st_ex_btime",
447 : timeval_string(talloc_tos(), &tc, true),
448 : timeval_string(talloc_tos(), &ts, true)));
449 0 : return false;
450 : }
451 :
452 0 : if (cookie_st->st_ex_iflags != fsp_st->st_ex_iflags) {
453 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
454 : "stat_ex.%s differs: "
455 : "cookie:%llu != stat:%llu, "
456 : "denying durable reconnect\n",
457 : name,
458 : "st_ex_calculated_birthtime",
459 : (unsigned long long)cookie_st->st_ex_iflags,
460 : (unsigned long long)fsp_st->st_ex_iflags));
461 0 : return false;
462 : }
463 :
464 0 : if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
465 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
466 : "stat_ex.%s differs: "
467 : "cookie:%llu != stat:%llu, "
468 : "denying durable reconnect\n",
469 : name,
470 : "st_ex_blksize",
471 : (unsigned long long)cookie_st->st_ex_blksize,
472 : (unsigned long long)fsp_st->st_ex_blksize));
473 0 : return false;
474 : }
475 :
476 0 : if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
477 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
478 : "stat_ex.%s differs: "
479 : "cookie:%llu != stat:%llu, "
480 : "denying durable reconnect\n",
481 : name,
482 : "st_ex_blocks",
483 : (unsigned long long)cookie_st->st_ex_blocks,
484 : (unsigned long long)fsp_st->st_ex_blocks));
485 0 : return false;
486 : }
487 :
488 0 : if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
489 0 : DEBUG(1, ("vfs_default_durable_reconnect (%s): "
490 : "stat_ex.%s differs: "
491 : "cookie:%llu != stat:%llu, "
492 : "denying durable reconnect\n",
493 : name,
494 : "st_ex_flags",
495 : (unsigned long long)cookie_st->st_ex_flags,
496 : (unsigned long long)fsp_st->st_ex_flags));
497 0 : return false;
498 : }
499 :
500 0 : return true;
501 : }
502 :
503 0 : static bool durable_reconnect_fn(
504 : struct share_mode_entry *e,
505 : bool *modified,
506 : void *private_data)
507 : {
508 0 : struct share_mode_entry *dst_e = private_data;
509 :
510 0 : if (dst_e->pid.pid != 0) {
511 0 : DBG_INFO("Found more than one entry, invalidating previous\n");
512 0 : dst_e->pid.pid = 0;
513 0 : return true; /* end the loop through share mode entries */
514 : }
515 0 : *dst_e = *e;
516 0 : return false; /* Look at potential other entries */
517 : }
518 :
519 0 : NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
520 : struct smb_request *smb1req,
521 : struct smbXsrv_open *op,
522 : const DATA_BLOB old_cookie,
523 : TALLOC_CTX *mem_ctx,
524 : files_struct **result,
525 : DATA_BLOB *new_cookie)
526 : {
527 0 : const struct loadparm_substitution *lp_sub =
528 0 : loadparm_s3_global_substitution();
529 : struct share_mode_lock *lck;
530 : struct share_mode_entry e;
531 0 : struct files_struct *fsp = NULL;
532 : NTSTATUS status;
533 : bool ok;
534 : int ret;
535 0 : struct vfs_open_how how = { .flags = 0, };
536 : struct file_id file_id;
537 0 : struct smb_filename *smb_fname = NULL;
538 : enum ndr_err_code ndr_err;
539 : struct vfs_default_durable_cookie cookie;
540 0 : DATA_BLOB new_cookie_blob = data_blob_null;
541 :
542 0 : *result = NULL;
543 0 : *new_cookie = data_blob_null;
544 :
545 0 : if (!lp_durable_handles(SNUM(conn))) {
546 0 : return NT_STATUS_NOT_SUPPORTED;
547 : }
548 :
549 : /*
550 : * the checks for kernel oplocks
551 : * and similar things are done
552 : * in the vfs_default_durable_cookie()
553 : * call below.
554 : */
555 :
556 0 : ndr_err = ndr_pull_struct_blob_all(
557 : &old_cookie,
558 : talloc_tos(),
559 : &cookie,
560 : (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
561 0 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
562 0 : status = ndr_map_error2ntstatus(ndr_err);
563 0 : return status;
564 : }
565 :
566 0 : if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
567 0 : return NT_STATUS_INVALID_PARAMETER;
568 : }
569 :
570 0 : if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
571 0 : return NT_STATUS_INVALID_PARAMETER;
572 : }
573 :
574 0 : if (!cookie.allow_reconnect) {
575 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
576 : }
577 :
578 0 : if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
579 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
580 : }
581 :
582 : /* Create an smb_filename with stream_name == NULL. */
583 0 : smb_fname = synthetic_smb_fname(talloc_tos(),
584 : cookie.base_name,
585 : NULL,
586 : NULL,
587 : 0,
588 : 0);
589 0 : if (smb_fname == NULL) {
590 0 : return NT_STATUS_NO_MEMORY;
591 : }
592 :
593 0 : ret = SMB_VFS_LSTAT(conn, smb_fname);
594 0 : if (ret == -1) {
595 0 : status = map_nt_error_from_unix_common(errno);
596 0 : DEBUG(1, ("Unable to lstat stream: %s => %s\n",
597 : smb_fname_str_dbg(smb_fname),
598 : nt_errstr(status)));
599 0 : return status;
600 : }
601 :
602 0 : if (!S_ISREG(smb_fname->st.st_ex_mode)) {
603 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
604 : }
605 :
606 0 : file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
607 0 : if (!file_id_equal(&cookie.id, &file_id)) {
608 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
609 : }
610 :
611 : /*
612 : * 1. check entry in locking.tdb
613 : */
614 :
615 0 : lck = get_existing_share_mode_lock(mem_ctx, file_id);
616 0 : if (lck == NULL) {
617 0 : DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
618 : "not obtained from db\n"));
619 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
620 : }
621 :
622 0 : e = (struct share_mode_entry) { .pid.pid = 0 };
623 :
624 0 : ok = share_mode_forall_entries(lck, durable_reconnect_fn, &e);
625 0 : if (!ok) {
626 0 : DBG_WARNING("share_mode_forall_entries failed\n");
627 0 : TALLOC_FREE(lck);
628 0 : return NT_STATUS_INTERNAL_DB_ERROR;
629 : }
630 :
631 0 : if (e.pid.pid == 0) {
632 0 : DBG_WARNING("Did not find a unique valid share mode entry\n");
633 0 : TALLOC_FREE(lck);
634 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
635 : }
636 :
637 0 : if (!server_id_is_disconnected(&e.pid)) {
638 0 : DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
639 : "reconnect for handle that was not marked "
640 : "disconnected (e.g. smbd or cluster node died)\n"));
641 0 : TALLOC_FREE(lck);
642 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
643 : }
644 :
645 0 : if (e.share_file_id != op->global->open_persistent_id) {
646 0 : DBG_INFO("denying durable "
647 : "share_file_id changed %"PRIu64" != %"PRIu64" "
648 : "(e.g. another client had opened the file)\n",
649 : e.share_file_id,
650 : op->global->open_persistent_id);
651 0 : TALLOC_FREE(lck);
652 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
653 : }
654 :
655 0 : if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
656 0 : !CAN_WRITE(conn))
657 : {
658 0 : DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
659 : "share[%s] is not writeable anymore\n",
660 : lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
661 0 : TALLOC_FREE(lck);
662 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
663 : }
664 :
665 : /*
666 : * 2. proceed with opening file
667 : */
668 :
669 0 : status = fsp_new(conn, conn, &fsp);
670 0 : if (!NT_STATUS_IS_OK(status)) {
671 0 : DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
672 : "new fsp: %s\n", nt_errstr(status)));
673 0 : TALLOC_FREE(lck);
674 0 : return status;
675 : }
676 :
677 0 : fh_set_private_options(fsp->fh, e.private_options);
678 0 : fsp->file_id = file_id;
679 0 : fsp->file_pid = smb1req->smbpid;
680 0 : fsp->vuid = smb1req->vuid;
681 0 : fsp->open_time = e.time;
682 0 : fsp->access_mask = e.access_mask;
683 0 : fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0);
684 0 : fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
685 0 : fsp->fnum = op->local_id;
686 0 : fsp_set_gen_id(fsp);
687 :
688 : /*
689 : * TODO:
690 : * Do we need to store the modified flag in the DB?
691 : */
692 0 : fsp->fsp_flags.modified = false;
693 : /*
694 : * no durables for directories
695 : */
696 0 : fsp->fsp_flags.is_directory = false;
697 : /*
698 : * For normal files, can_lock == !is_directory
699 : */
700 0 : fsp->fsp_flags.can_lock = true;
701 : /*
702 : * We do not support aio write behind for smb2
703 : */
704 0 : fsp->fsp_flags.aio_write_behind = false;
705 0 : fsp->oplock_type = e.op_type;
706 :
707 0 : if (fsp->oplock_type == LEASE_OPLOCK) {
708 : uint32_t current_state;
709 : uint16_t lease_version, epoch;
710 :
711 : /*
712 : * Ensure the existing client guid matches the
713 : * stored one in the share_mode_entry.
714 : */
715 0 : if (!GUID_equal(fsp_client_guid(fsp),
716 : &e.client_guid)) {
717 0 : TALLOC_FREE(lck);
718 0 : file_free(smb1req, fsp);
719 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
720 : }
721 :
722 0 : status = leases_db_get(
723 : &e.client_guid,
724 : &e.lease_key,
725 : &file_id,
726 : ¤t_state, /* current_state */
727 : NULL, /* breaking */
728 : NULL, /* breaking_to_requested */
729 : NULL, /* breaking_to_required */
730 : &lease_version, /* lease_version */
731 : &epoch); /* epoch */
732 0 : if (!NT_STATUS_IS_OK(status)) {
733 0 : TALLOC_FREE(lck);
734 0 : file_free(smb1req, fsp);
735 0 : return status;
736 : }
737 :
738 0 : fsp->lease = find_fsp_lease(
739 : fsp,
740 : &e.lease_key,
741 : current_state,
742 : lease_version,
743 : epoch);
744 0 : if (fsp->lease == NULL) {
745 0 : TALLOC_FREE(lck);
746 0 : file_free(smb1req, fsp);
747 0 : return NT_STATUS_NO_MEMORY;
748 : }
749 : }
750 :
751 0 : fsp->initial_allocation_size = cookie.initial_allocation_size;
752 0 : fh_set_position_information(fsp->fh, cookie.position_information);
753 0 : fsp->fsp_flags.update_write_time_triggered =
754 0 : cookie.update_write_time_triggered;
755 0 : fsp->fsp_flags.update_write_time_on_close =
756 0 : cookie.update_write_time_on_close;
757 0 : fsp->fsp_flags.write_time_forced = cookie.write_time_forced;
758 0 : fsp->close_write_time = nt_time_to_full_timespec(
759 : cookie.close_write_time);
760 :
761 0 : status = fsp_set_smb_fname(fsp, smb_fname);
762 0 : if (!NT_STATUS_IS_OK(status)) {
763 0 : TALLOC_FREE(lck);
764 0 : file_free(smb1req, fsp);
765 0 : DEBUG(0, ("vfs_default_durable_reconnect: "
766 : "fsp_set_smb_fname failed: %s\n",
767 : nt_errstr(status)));
768 0 : return status;
769 : }
770 :
771 0 : op->compat = fsp;
772 0 : fsp->op = op;
773 :
774 0 : ok = reset_share_mode_entry(
775 : lck,
776 : e.pid,
777 : e.share_file_id,
778 0 : messaging_server_id(conn->sconn->msg_ctx),
779 : smb1req->mid,
780 0 : fh_get_gen_id(fsp->fh));
781 0 : if (!ok) {
782 0 : DBG_DEBUG("Could not set new share_mode_entry values\n");
783 0 : TALLOC_FREE(lck);
784 0 : op->compat = NULL;
785 0 : fsp->op = NULL;
786 0 : file_free(smb1req, fsp);
787 0 : return NT_STATUS_INTERNAL_ERROR;
788 : }
789 :
790 0 : ok = brl_reconnect_disconnected(fsp);
791 0 : if (!ok) {
792 0 : status = NT_STATUS_INTERNAL_ERROR;
793 0 : DEBUG(1, ("vfs_default_durable_reconnect: "
794 : "failed to reopen brlocks: %s\n",
795 : nt_errstr(status)));
796 0 : TALLOC_FREE(lck);
797 0 : op->compat = NULL;
798 0 : fsp->op = NULL;
799 0 : file_free(smb1req, fsp);
800 0 : return status;
801 : }
802 :
803 : /*
804 : * TODO: properly calculate open flags
805 : */
806 0 : if (fsp->fsp_flags.can_write && fsp->fsp_flags.can_read) {
807 0 : how.flags = O_RDWR;
808 0 : } else if (fsp->fsp_flags.can_write) {
809 0 : how.flags = O_WRONLY;
810 0 : } else if (fsp->fsp_flags.can_read) {
811 0 : how.flags = O_RDONLY;
812 : }
813 :
814 0 : status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, &how);
815 0 : if (!NT_STATUS_IS_OK(status)) {
816 0 : TALLOC_FREE(lck);
817 0 : DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
818 : "file: %s\n", nt_errstr(status)));
819 0 : op->compat = NULL;
820 0 : fsp->op = NULL;
821 0 : file_free(smb1req, fsp);
822 0 : return status;
823 : }
824 :
825 : /*
826 : * We now check the stat info stored in the cookie against
827 : * the current stat data from the file we just opened.
828 : * If any detail differs, we deny the durable reconnect,
829 : * because in that case it is very likely that someone
830 : * opened the file while the handle was disconnected,
831 : * which has to be interpreted as an oplock break.
832 : */
833 :
834 0 : ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
835 0 : if (ret == -1) {
836 : NTSTATUS close_status;
837 0 : status = map_nt_error_from_unix_common(errno);
838 0 : DEBUG(1, ("Unable to fstat stream: %s => %s\n",
839 : smb_fname_str_dbg(smb_fname),
840 : nt_errstr(status)));
841 0 : close_status = fd_close(fsp);
842 0 : if (!NT_STATUS_IS_OK(close_status)) {
843 0 : DBG_ERR("fd_close failed (%s) - leaking file "
844 : "descriptor\n", nt_errstr(close_status));
845 : }
846 0 : TALLOC_FREE(lck);
847 0 : op->compat = NULL;
848 0 : fsp->op = NULL;
849 0 : file_free(smb1req, fsp);
850 0 : return status;
851 : }
852 :
853 0 : if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
854 0 : NTSTATUS close_status = fd_close(fsp);
855 0 : if (!NT_STATUS_IS_OK(close_status)) {
856 0 : DBG_ERR("fd_close failed (%s) - leaking file "
857 : "descriptor\n", nt_errstr(close_status));
858 : }
859 0 : TALLOC_FREE(lck);
860 0 : op->compat = NULL;
861 0 : fsp->op = NULL;
862 0 : file_free(smb1req, fsp);
863 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
864 : }
865 :
866 0 : file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
867 0 : if (!file_id_equal(&cookie.id, &file_id)) {
868 0 : NTSTATUS close_status = fd_close(fsp);
869 0 : if (!NT_STATUS_IS_OK(close_status)) {
870 0 : DBG_ERR("fd_close failed (%s) - leaking file "
871 : "descriptor\n", nt_errstr(close_status));
872 : }
873 0 : TALLOC_FREE(lck);
874 0 : op->compat = NULL;
875 0 : fsp->op = NULL;
876 0 : file_free(smb1req, fsp);
877 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
878 : }
879 :
880 0 : (void)fdos_mode(fsp);
881 :
882 0 : ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
883 0 : &fsp->fsp_name->st,
884 : fsp_str_dbg(fsp));
885 0 : if (!ok) {
886 0 : NTSTATUS close_status = fd_close(fsp);
887 0 : if (!NT_STATUS_IS_OK(close_status)) {
888 0 : DBG_ERR("fd_close failed (%s) - leaking file "
889 : "descriptor\n", nt_errstr(close_status));
890 : }
891 0 : TALLOC_FREE(lck);
892 0 : op->compat = NULL;
893 0 : fsp->op = NULL;
894 0 : file_free(smb1req, fsp);
895 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
896 : }
897 :
898 0 : status = set_file_oplock(fsp);
899 0 : if (!NT_STATUS_IS_OK(status)) {
900 0 : NTSTATUS close_status = fd_close(fsp);
901 0 : if (!NT_STATUS_IS_OK(close_status)) {
902 0 : DBG_ERR("fd_close failed (%s) - leaking file "
903 : "descriptor\n", nt_errstr(close_status));
904 : }
905 0 : TALLOC_FREE(lck);
906 0 : op->compat = NULL;
907 0 : fsp->op = NULL;
908 0 : file_free(smb1req, fsp);
909 0 : return status;
910 : }
911 :
912 0 : status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
913 0 : if (!NT_STATUS_IS_OK(status)) {
914 0 : TALLOC_FREE(lck);
915 0 : DEBUG(1, ("vfs_default_durable_reconnect: "
916 : "vfs_default_durable_cookie - %s\n",
917 : nt_errstr(status)));
918 0 : op->compat = NULL;
919 0 : fsp->op = NULL;
920 0 : file_free(smb1req, fsp);
921 0 : return status;
922 : }
923 :
924 0 : smb1req->chain_fsp = fsp;
925 0 : smb1req->smb2req->compat_chain_fsp = fsp;
926 :
927 0 : DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
928 : fsp_str_dbg(fsp)));
929 :
930 0 : TALLOC_FREE(lck);
931 :
932 0 : fsp->fsp_flags.is_fsa = true;
933 :
934 0 : *result = fsp;
935 0 : *new_cookie = new_cookie_blob;
936 :
937 0 : return NT_STATUS_OK;
938 : }
|