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 680 : NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
37 : TALLOC_CTX *mem_ctx,
38 : DATA_BLOB *cookie_blob)
39 : {
40 680 : struct connection_struct *conn = fsp->conn;
41 0 : enum ndr_err_code ndr_err;
42 0 : struct vfs_default_durable_cookie cookie;
43 :
44 680 : if (!lp_durable_handles(SNUM(conn))) {
45 0 : return NT_STATUS_NOT_SUPPORTED;
46 : }
47 :
48 680 : 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 680 : 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 686 : if ((fsp->current_lock_count > 0) &&
65 6 : 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 680 : if (fsp->fsp_flags.is_directory) {
75 0 : return NT_STATUS_NOT_SUPPORTED;
76 : }
77 :
78 680 : 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 680 : 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 680 : ZERO_STRUCT(cookie);
95 680 : cookie.allow_reconnect = false;
96 680 : cookie.id = fsp->file_id;
97 680 : cookie.servicepath = conn->connectpath;
98 680 : cookie.base_name = fsp->fsp_name->base_name;
99 680 : cookie.initial_allocation_size = fsp->initial_allocation_size;
100 680 : cookie.position_information = fh_get_position_information(fsp->fh);
101 680 : cookie.update_write_time_triggered =
102 680 : fsp->fsp_flags.update_write_time_triggered;
103 680 : cookie.update_write_time_on_close =
104 680 : fsp->fsp_flags.update_write_time_on_close;
105 680 : cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
106 1360 : cookie.close_write_time = full_timespec_to_nt_time(
107 680 : &fsp->close_write_time);
108 :
109 680 : cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
110 680 : cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
111 680 : cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
112 680 : cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
113 680 : cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
114 680 : cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
115 680 : cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
116 680 : cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
117 680 : cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
118 680 : cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
119 680 : cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
120 680 : cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
121 680 : cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
122 680 : cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
123 680 : cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
124 680 : cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
125 :
126 680 : ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
127 : (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
128 680 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
129 0 : NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
130 0 : return status;
131 : }
132 :
133 680 : return NT_STATUS_OK;
134 : }
135 :
136 168 : 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 168 : struct connection_struct *conn = fsp->conn;
142 0 : NTSTATUS status;
143 0 : enum ndr_err_code ndr_err;
144 0 : struct vfs_default_durable_cookie cookie;
145 168 : DATA_BLOB new_cookie_blob = data_blob_null;
146 0 : struct share_mode_lock *lck;
147 0 : bool ok;
148 :
149 168 : *new_cookie = data_blob_null;
150 :
151 168 : ZERO_STRUCT(cookie);
152 :
153 168 : ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
154 : (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
155 168 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
156 0 : status = ndr_map_error2ntstatus(ndr_err);
157 0 : return status;
158 : }
159 :
160 168 : if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
161 0 : return NT_STATUS_INVALID_PARAMETER;
162 : }
163 :
164 168 : if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
165 0 : return NT_STATUS_INVALID_PARAMETER;
166 : }
167 :
168 168 : if (!file_id_equal(&fsp->file_id, &cookie.id)) {
169 0 : return NT_STATUS_INVALID_PARAMETER;
170 : }
171 :
172 168 : 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 168 : if (fsp->fsp_flags.initial_delete_on_close) {
181 8 : return NT_STATUS_NOT_SUPPORTED;
182 : }
183 160 : if (fsp->fsp_flags.delete_on_close) {
184 0 : return NT_STATUS_NOT_SUPPORTED;
185 : }
186 :
187 160 : if (!VALID_STAT(fsp->fsp_name->st)) {
188 0 : return NT_STATUS_NOT_SUPPORTED;
189 : }
190 :
191 160 : 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 160 : if (fsp->update_write_time_event) {
197 18 : 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 160 : lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
205 160 : if (lck != NULL) {
206 0 : struct smb_file_time ft;
207 :
208 160 : init_smb_file_time(&ft);
209 :
210 160 : 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 160 : } else if (fsp->fsp_flags.update_write_time_on_close) {
214 4 : if (is_omit_timespec(&fsp->close_write_time)) {
215 4 : ft.mtime = timespec_current();
216 : } else {
217 0 : ft.mtime = fsp->close_write_time;
218 : }
219 : }
220 :
221 160 : if (!is_omit_timespec(&ft.mtime)) {
222 4 : round_timespec(conn->ts_res, &ft.mtime);
223 4 : file_ntimes(conn, fsp, &ft);
224 : }
225 :
226 160 : ok = mark_share_mode_disconnected(lck, fsp);
227 160 : if (!ok) {
228 0 : TALLOC_FREE(lck);
229 : }
230 : }
231 160 : if (lck != NULL) {
232 160 : ok = brl_mark_disconnected(fsp);
233 160 : if (!ok) {
234 0 : TALLOC_FREE(lck);
235 : }
236 : }
237 160 : if (lck == NULL) {
238 0 : return NT_STATUS_NOT_SUPPORTED;
239 : }
240 160 : TALLOC_FREE(lck);
241 :
242 160 : status = vfs_stat_fsp(fsp);
243 160 : if (!NT_STATUS_IS_OK(status)) {
244 0 : return status;
245 : }
246 :
247 160 : ZERO_STRUCT(cookie);
248 160 : cookie.allow_reconnect = true;
249 160 : cookie.id = fsp->file_id;
250 160 : cookie.servicepath = conn->connectpath;
251 160 : cookie.base_name = fsp_str_dbg(fsp);
252 160 : cookie.initial_allocation_size = fsp->initial_allocation_size;
253 160 : cookie.position_information = fh_get_position_information(fsp->fh);
254 160 : cookie.update_write_time_triggered =
255 160 : fsp->fsp_flags.update_write_time_triggered;
256 160 : cookie.update_write_time_on_close =
257 160 : fsp->fsp_flags.update_write_time_on_close;
258 160 : cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
259 320 : cookie.close_write_time = full_timespec_to_nt_time(
260 160 : &fsp->close_write_time);
261 :
262 160 : cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
263 160 : cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
264 160 : cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
265 160 : cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
266 160 : cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
267 160 : cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
268 160 : cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
269 160 : cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
270 160 : cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
271 160 : cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
272 160 : cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
273 160 : cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
274 160 : cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
275 160 : cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
276 160 : cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
277 160 : cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
278 :
279 160 : 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 160 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
282 0 : status = ndr_map_error2ntstatus(ndr_err);
283 0 : return status;
284 : }
285 :
286 160 : status = fd_close(fsp);
287 160 : if (!NT_STATUS_IS_OK(status)) {
288 0 : data_blob_free(&new_cookie_blob);
289 0 : return status;
290 : }
291 :
292 160 : *new_cookie = new_cookie_blob;
293 160 : 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 130 : 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 0 : int ret;
307 :
308 130 : 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 130 : 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 130 : 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 130 : 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 130 : 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 130 : 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 130 : ret = timespec_compare(&cookie_st->st_ex_atime,
381 130 : &fsp_st->st_ex_atime);
382 130 : if (ret != 0) {
383 0 : 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 130 : ret = timespec_compare(&cookie_st->st_ex_mtime,
399 130 : &fsp_st->st_ex_mtime);
400 130 : if (ret != 0) {
401 0 : 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 130 : ret = timespec_compare(&cookie_st->st_ex_ctime,
417 130 : &fsp_st->st_ex_ctime);
418 130 : if (ret != 0) {
419 0 : 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 130 : ret = timespec_compare(&cookie_st->st_ex_btime,
435 130 : &fsp_st->st_ex_btime);
436 130 : if (ret != 0) {
437 0 : 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 130 : 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 130 : 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 130 : 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 130 : if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
489 0 : DBG_WARNING(" (%s): "
490 : "stat_ex.%s differs: "
491 : "cookie:%"PRIu32" != stat:%"PRIu32", "
492 : "denying durable reconnect\n",
493 : name,
494 : "st_ex_flags",
495 : cookie_st->st_ex_flags,
496 : fsp_st->st_ex_flags);
497 0 : return false;
498 : }
499 :
500 130 : return true;
501 : }
502 :
503 150 : static bool durable_reconnect_fn(
504 : struct share_mode_entry *e,
505 : bool *modified,
506 : void *private_data)
507 : {
508 150 : struct share_mode_entry *dst_e = private_data;
509 :
510 150 : 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 150 : *dst_e = *e;
516 150 : return false; /* Look at potential other entries */
517 : }
518 :
519 158 : 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 158 : loadparm_s3_global_substitution();
529 0 : struct share_mode_lock *lck;
530 0 : struct share_mode_entry e;
531 158 : struct files_struct *fsp = NULL;
532 0 : NTSTATUS status;
533 0 : bool ok;
534 0 : int ret;
535 158 : struct vfs_open_how how = { .flags = 0, };
536 0 : struct file_id file_id;
537 158 : struct smb_filename *smb_fname = NULL;
538 0 : enum ndr_err_code ndr_err;
539 0 : struct vfs_default_durable_cookie cookie;
540 158 : DATA_BLOB new_cookie_blob = data_blob_null;
541 :
542 158 : *result = NULL;
543 158 : *new_cookie = data_blob_null;
544 :
545 158 : 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 158 : 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 158 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
562 0 : status = ndr_map_error2ntstatus(ndr_err);
563 0 : return status;
564 : }
565 :
566 158 : if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
567 0 : return NT_STATUS_INVALID_PARAMETER;
568 : }
569 :
570 158 : if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
571 0 : return NT_STATUS_INVALID_PARAMETER;
572 : }
573 :
574 158 : if (!cookie.allow_reconnect) {
575 8 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
576 : }
577 :
578 150 : 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 150 : smb_fname = synthetic_smb_fname(talloc_tos(),
584 : cookie.base_name,
585 : NULL,
586 : NULL,
587 : 0,
588 : 0);
589 150 : if (smb_fname == NULL) {
590 0 : return NT_STATUS_NO_MEMORY;
591 : }
592 :
593 150 : ret = SMB_VFS_LSTAT(conn, smb_fname);
594 150 : 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 150 : if (!S_ISREG(smb_fname->st.st_ex_mode)) {
603 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
604 : }
605 :
606 150 : file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
607 150 : 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 150 : lck = get_existing_share_mode_lock(mem_ctx, file_id);
616 150 : 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 150 : e = (struct share_mode_entry) { .pid.pid = 0 };
623 :
624 150 : ok = share_mode_forall_entries(lck, durable_reconnect_fn, &e);
625 150 : 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 150 : 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 150 : if (!server_id_is_disconnected(&e.pid)) {
638 12 : 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 12 : TALLOC_FREE(lck);
642 12 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
643 : }
644 :
645 138 : 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 138 : if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
656 138 : !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 138 : status = fsp_new(conn, conn, &fsp);
670 138 : 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 138 : fh_set_private_options(fsp->fh, e.private_options);
678 138 : fsp->file_id = file_id;
679 138 : fsp->file_pid = smb1req->smbpid;
680 138 : fsp->vuid = smb1req->vuid;
681 138 : fsp->open_time = e.time;
682 138 : fsp->access_mask = e.access_mask;
683 138 : fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0);
684 138 : fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
685 138 : fsp->fnum = op->local_id;
686 138 : fsp_set_gen_id(fsp);
687 :
688 : /*
689 : * TODO:
690 : * Do we need to store the modified flag in the DB?
691 : */
692 138 : fsp->fsp_flags.modified = false;
693 : /*
694 : * no durables for directories
695 : */
696 138 : fsp->fsp_flags.is_directory = false;
697 : /*
698 : * For normal files, can_lock == !is_directory
699 : */
700 138 : fsp->fsp_flags.can_lock = true;
701 : /*
702 : * We do not support aio write behind for smb2
703 : */
704 138 : fsp->fsp_flags.aio_write_behind = false;
705 138 : fsp->oplock_type = e.op_type;
706 :
707 138 : if (fsp->oplock_type == LEASE_OPLOCK) {
708 0 : uint32_t current_state;
709 0 : 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 60 : if (!GUID_equal(fsp_client_guid(fsp),
716 : &e.client_guid)) {
717 8 : TALLOC_FREE(lck);
718 8 : file_free(smb1req, fsp);
719 8 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
720 : }
721 :
722 52 : 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 52 : if (!NT_STATUS_IS_OK(status)) {
733 0 : TALLOC_FREE(lck);
734 0 : file_free(smb1req, fsp);
735 0 : return status;
736 : }
737 :
738 52 : fsp->lease = find_fsp_lease(
739 : fsp,
740 : &e.lease_key,
741 : current_state,
742 : lease_version,
743 : epoch);
744 52 : 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 130 : fsp->initial_allocation_size = cookie.initial_allocation_size;
752 130 : fh_set_position_information(fsp->fh, cookie.position_information);
753 130 : fsp->fsp_flags.update_write_time_triggered =
754 130 : cookie.update_write_time_triggered;
755 130 : fsp->fsp_flags.update_write_time_on_close =
756 130 : cookie.update_write_time_on_close;
757 130 : fsp->fsp_flags.write_time_forced = cookie.write_time_forced;
758 130 : fsp->close_write_time = nt_time_to_full_timespec(
759 : cookie.close_write_time);
760 :
761 130 : status = fsp_set_smb_fname(fsp, smb_fname);
762 130 : 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 130 : op->compat = fsp;
772 130 : fsp->op = op;
773 :
774 130 : ok = reset_share_mode_entry(
775 : lck,
776 : e.pid,
777 : e.share_file_id,
778 130 : messaging_server_id(conn->sconn->msg_ctx),
779 : smb1req->mid,
780 130 : fh_get_gen_id(fsp->fh));
781 130 : 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 130 : ok = brl_reconnect_disconnected(fsp);
791 130 : 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 130 : if (fsp->fsp_flags.can_write && fsp->fsp_flags.can_read) {
807 130 : 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 130 : status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, &how);
815 130 : 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 130 : ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
835 130 : if (ret == -1) {
836 0 : 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 130 : 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 130 : file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
867 130 : 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 130 : (void)fdos_mode(fsp);
881 :
882 130 : ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
883 130 : &fsp->fsp_name->st,
884 : fsp_str_dbg(fsp));
885 130 : 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 130 : status = set_file_oplock(fsp);
899 130 : 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 130 : status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
913 130 : 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 130 : smb1req->chain_fsp = fsp;
925 130 : smb1req->smb2req->compat_chain_fsp = fsp;
926 :
927 130 : DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
928 : fsp_str_dbg(fsp)));
929 :
930 130 : TALLOC_FREE(lck);
931 :
932 130 : fsp->fsp_flags.is_fsa = true;
933 :
934 130 : *result = fsp;
935 130 : *new_cookie = new_cookie_blob;
936 :
937 130 : return NT_STATUS_OK;
938 : }
|