Print this page
NEX-19647 panic in smb_notify_encode_action
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-18761 panic in smb_ofile_free with vdbench
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-17431 (HyperV) Windows VM goes panic after failover
NEX-18639 Panic in smb_ofile_release with expired ofile
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-15958 panic importing CA share after failover
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Include in backports of:
NEX-9808 SMB3 persistent handles
NEX-15958 panic importing CA share after failover
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Include in backports of:
NEX-9808 SMB3 persistent handles
NEX-15931 Panic removing files in SMB3 CA share
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Include in backports of:
NEX-9808 SMB3 persistent handles
NEX-15931 Panic removing files in SMB3 CA share
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Include in backports of:
NEX-9808 SMB3 persistent handles
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-13653 Obsolete SMB server work-around for ZFS read-only
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5273 SMB 3 Encryption
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-9360 SMB1 fails renaming an open file
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
NEX-8495 Panic after SMB flush on a named pipe
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5844 want SMB2 ioctl FSCTL_SRV_COPYCHUNK
NEX-6124 smb_fsop_read/write should allow file != sr->fid_ofile
NEX-6125 smbtorture invalid response with smb2.ioctl
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-6096 Enable compile warnings re. parentheses in smbsrv
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
NEX-6042 SMB resilient handle lock replay
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5977 smbtorture smb2.notify.mask fails after NEX-3553
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3553 SMB2/3 durable handles
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5586 SMB2 ofiles need real Persistent IDs
NEX-5313 SMB2 oplock break notification should use TID=0
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-5537 Want reference counts for users, trees...
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-5312 delete_on_close should be acted on earlier
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-3906 Prefer that SMB change notify not tie up a worker thread
NEX-5278 SMB notify should buffer per file handle
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-3432 CLONE - NEX-3232 Symantec Backup Exec fails opening files over SMB
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Daniel Borek <daniel.borek@nexenta.com>
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-39 Use AF_UNIX pipes for RPC
SMB-65 SMB server in non-global zones (use zone_kcred())
SMB-65 SMB server in non-global zones (kmem_caches)
common kmem_cache instances across zones
separate GZ-only init from NGZ init
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
re #7815 SMB server delivers old modification time... (fix allocsz)
re #7815 SMB server delivers old modification time...
re #11215 rb3676 sesctl to SGI JBOD hangs in biowait() with a command stuck in mptsas driver
re #10734 NT Trans. Notify returning too quickly
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ new/usr/src/uts/common/fs/smbsrv/smb_ofile.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 - * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 23 * Copyright 2016 Syneto S.R.L. All rights reserved.
25 24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 + * Copyright 2019 Nexenta Systems, Inc. All rights reserved.
26 26 */
27 27
28 28 /*
29 29 * General Structures Layout
30 30 * -------------------------
31 31 *
32 32 * This is a simplified diagram showing the relationship between most of the
33 33 * main structures.
34 34 *
35 35 * +-------------------+
36 36 * | SMB_INFO |
37 37 * +-------------------+
38 38 * |
39 39 * |
40 40 * v
41 41 * +-------------------+ +-------------------+ +-------------------+
42 42 * | SESSION |<----->| SESSION |......| SESSION |
43 43 * +-------------------+ +-------------------+ +-------------------+
44 44 * | |
45 45 * | |
46 46 * | v
47 47 * | +-------------------+ +-------------------+ +-------------------+
48 48 * | | USER |<--->| USER |...| USER |
49 49 * | +-------------------+ +-------------------+ +-------------------+
50 50 * |
51 51 * |
52 52 * v
53 53 * +-------------------+ +-------------------+ +-------------------+
54 54 * | TREE |<----->| TREE |......| TREE |
55 55 * +-------------------+ +-------------------+ +-------------------+
56 56 * | |
57 57 * | |
58 58 * | v
59 59 * | +-------+ +-------+ +-------+
60 60 * | | OFILE |<----->| OFILE |......| OFILE |
61 61 * | +-------+ +-------+ +-------+
62 62 * |
63 63 * |
|
↓ open down ↓ |
28 lines elided |
↑ open up ↑ |
64 64 * v
65 65 * +-------+ +------+ +------+
66 66 * | ODIR |<----->| ODIR |......| ODIR |
67 67 * +-------+ +------+ +------+
68 68 *
69 69 *
70 70 * Ofile State Machine
71 71 * ------------------
72 72 *
73 73 * +-------------------------+ T0
74 - * | SMB_OFILE_STATE_OPEN |<----------- Creation/Allocation
74 + * | SMB_OFILE_STATE_OPEN |<--+-------- Creation/Allocation
75 + * +-------------------------+ |
76 + * | | | T5
77 + * | | +---------------------------+
78 + * | | | SMB_OFILE_STATE_RECONNECT |
79 + * | | +---------------------------+
80 + * | | ^
81 + * | v |
82 + * | +---------------+ |
83 + * | | STATE_SAVE_DH | |
84 + * | | STATE_SAVING | |
85 + * | +---------------+ |
86 + * | | | T4
87 + * | T1 | T3 +--------------------------+
88 + * | +------>| SMB_OFILE_STATE_ORPHANED |
89 + * v +--------------------------+
90 + * +-------------------------+ | |
91 + * | SMB_OFILE_STATE_CLOSING |<--+ T6 | T7
92 + * +-------------------------+ |
93 + * | ^ v
94 + * | T2 | T8 +-------------------------+
95 + * | +-------| SMB_OFILE_STATE_EXPIRED |
96 + * v +-------------------------+
75 97 * +-------------------------+
76 - * |
77 - * | T1
78 - * |
79 - * v
80 - * +-------------------------+
81 - * | SMB_OFILE_STATE_CLOSING |
82 - * +-------------------------+
83 - * |
84 - * | T2
85 - * |
86 - * v
87 - * +-------------------------+ T3
88 98 * | SMB_OFILE_STATE_CLOSED |----------> Deletion/Free
89 - * +-------------------------+
99 + * +-------------------------+ T9
90 100 *
91 101 * SMB_OFILE_STATE_OPEN
92 102 *
93 103 * While in this state:
94 104 * - The ofile is queued in the list of ofiles of its tree.
95 105 * - References will be given out if the ofile is looked up.
96 106 *
107 + * SMB_OFILE_STATE_SAVE_DH
108 + *
109 + * Similar to state _CLOSING, but instead of deleting the ofile,
110 + * it leaves the ofile in state _ORPHANED (for later reclaim).
111 + * Will move to _SAVING after last ref, then _ORPHANED.
112 + *
113 + * While in this state:
114 + * - The ofile has been marked for preservation during a
115 + * walk of the tree ofile list to close multiple files.
116 + * - References will not be given out if the ofile is looked up,
117 + * except for oplock break processing.
118 + * - Still affects Sharing Violation rules
119 + *
120 + * SMB_OFILE_STATE_SAVING
121 + *
122 + * Transient state used to keep oplock break processing out
123 + * while the ofile moves to state _ORPHANED.
124 + *
125 + * While in this state:
126 + * - References will not be given out if the ofile is looked up,
127 + * except for oplock break processing.
128 + * - Still affects Sharing Violation rules
129 + *
97 130 * SMB_OFILE_STATE_CLOSING
98 131 *
132 + * Close has been requested. Stay in this state until the last
133 + * ref. is gone, then move to state _CLOSED
134 + *
99 135 * While in this state:
100 136 * - The ofile is queued in the list of ofiles of its tree.
101 137 * - References will not be given out if the ofile is looked up.
102 138 * - The file is closed and the locks held are being released.
103 139 * - The resources associated with the ofile remain.
104 140 *
105 141 * SMB_OFILE_STATE_CLOSED
106 142 *
107 143 * While in this state:
108 144 * - The ofile is queued in the list of ofiles of its tree.
109 145 * - References will not be given out if the ofile is looked up.
110 146 * - The resources associated with the ofile remain.
111 147 *
148 + * SMB_OFILE_STATE_ORPHANED
149 + *
150 + * While in this state:
151 + * - The ofile is queued in the list of ofiles of its tree.
152 + * - Can be reclaimed by the original owner
153 + * - References will not be given out if the ofile is looked up.
154 + * - All the tree, user, and session "up" pointers are NULL!
155 + * - Will eventually be "expired" if not reclaimed
156 + * - Can be closed if its oplock is broken
157 + * - Still affects Sharing Violation rules
158 + *
159 + * SMB_OFILE_STATE_EXPIRED
160 + *
161 + * While in this state:
162 + * - The ofile is queued in the list of ofiles of its tree.
163 + * - References will not be given out if the ofile is looked up.
164 + * - The ofile has not been reclaimed and will soon be closed,
165 + * due to, for example, the durable handle timer expiring, or its
166 + * oplock being broken.
167 + * - Cannot be reclaimed at this point
168 + *
169 + * SMB_OFILE_STATE_RECONNECT
170 + *
171 + * Transient state used to keep oplock break processing out
172 + * while the ofile moves from state _ORPHANED to _OPEN.
173 + *
174 + * While in this state:
175 + * - The ofile is being reclaimed; do not touch it.
176 + * - References will not be given out if the ofile is looked up.
177 + * - Still affects Sharing Violation rules
178 + * - see smb2_dh_reconnect() for which members need to be avoided
179 + *
112 180 * Transition T0
113 181 *
114 182 * This transition occurs in smb_ofile_open(). A new ofile is created and
115 183 * added to the list of ofiles of a tree.
116 184 *
117 185 * Transition T1
118 186 *
119 - * This transition occurs in smb_ofile_close().
187 + * This transition occurs in smb_ofile_close(). Note that this only happens
188 + * when we determine that an ofile should be closed in spite of its durable
189 + * handle properties.
120 190 *
121 191 * Transition T2
122 192 *
123 193 * This transition occurs in smb_ofile_release(). The resources associated
124 194 * with the ofile are freed as well as the ofile structure. For the
125 195 * transition to occur, the ofile must be in the SMB_OFILE_STATE_CLOSED
126 196 * state and the reference count be zero.
127 197 *
198 + * Transition T3
199 + *
200 + * This transition occurs in smb_ofile_orphan_dh(). It happens during an
201 + * smb2 logoff, or during a session disconnect when certain conditions are
202 + * met. The ofile and structures above it will be kept around until the ofile
203 + * either gets reclaimed, expires after f_timeout_offset nanoseconds, or its
204 + * oplock is broken.
205 + *
206 + * Transition T4
207 + *
208 + * This transition occurs in smb2_dh_reconnect(). An smb2 create request
209 + * with a DURABLE_HANDLE_RECONNECT(_V2) create context has been
210 + * recieved from the original owner. If leases are supported or it's
211 + * RECONNECT_V2, reconnect is subject to additional conditions. The ofile
212 + * will be unwired from the old, disconnected session, tree, and user,
213 + * and wired up to its new context.
214 + *
215 + * Transition T5
216 + *
217 + * This transition occurs in smb2_dh_reconnect(). The ofile has been
218 + * successfully reclaimed.
219 + *
220 + * Transition T6
221 + *
222 + * This transition occurs in smb_ofile_close(). The ofile has been orphaned
223 + * while some thread was blocked, and that thread closes the ofile. Can only
224 + * happen when the ofile is orphaned due to an SMB2 LOGOFF request.
225 + *
226 + * Transition T7
227 + *
228 + * This transition occurs in smb_session_durable_timers() and
229 + * smb_oplock_send_brk(). The ofile will soon be closed.
230 + * In the former case, f_timeout_offset nanoseconds have passed since
231 + * the ofile was orphaned. In the latter, an oplock break occured
232 + * on the ofile while it was orphaned.
233 + *
234 + * Transition T8
235 + *
236 + * This transition occurs in smb_ofile_close().
237 + *
238 + * Transition T9
239 + *
240 + * This transition occurs in smb_ofile_delete().
241 + *
128 242 * Comments
129 243 * --------
130 244 *
131 245 * The state machine of the ofile structures is controlled by 3 elements:
132 246 * - The list of ofiles of the tree it belongs to.
133 247 * - The mutex embedded in the structure itself.
134 248 * - The reference count.
135 249 *
136 250 * There's a mutex embedded in the ofile structure used to protect its fields
137 251 * and there's a lock embedded in the list of ofiles of a tree. To
138 252 * increment or to decrement the reference count the mutex must be entered.
139 253 * To insert the ofile into the list of ofiles of the tree and to remove
140 254 * the ofile from it, the lock must be entered in RW_WRITER mode.
141 255 *
142 256 * Rules of access to a ofile structure:
143 257 *
144 258 * 1) In order to avoid deadlocks, when both (mutex and lock of the ofile
145 - * list) have to be entered, the lock must be entered first.
259 + * list) have to be entered, the lock must be entered first. Additionally,
260 + * f_mutex must not be held when removing the ofile from sv_persistid_ht.
146 261 *
147 262 * 2) All actions applied to an ofile require a reference count.
148 263 *
149 264 * 3) There are 2 ways of getting a reference count. One is when the ofile
150 265 * is opened. The other one when the ofile is looked up. This translates
151 266 * into 2 functions: smb_ofile_open() and smb_ofile_lookup_by_fid().
152 267 *
153 268 * It should be noted that the reference count of an ofile registers the
154 269 * number of references to the ofile in other structures (such as an smb
155 270 * request). The reference count is not incremented in these 2 instances:
156 271 *
157 272 * 1) The ofile is open. An ofile is anchored by its state. If there's
158 273 * no activity involving an ofile currently open, the reference count
159 274 * of that ofile is zero.
160 275 *
161 276 * 2) The ofile is queued in the list of ofiles of its tree. The fact of
162 277 * being queued in that list is NOT registered by incrementing the
163 278 * reference count.
164 279 */
165 -#include <smbsrv/smb_kproto.h>
280 +#include <smbsrv/smb2_kproto.h>
166 281 #include <smbsrv/smb_fsops.h>
282 +#include <sys/time.h>
283 +#include <sys/random.h>
167 284
168 285 static boolean_t smb_ofile_is_open_locked(smb_ofile_t *);
169 -static smb_ofile_t *smb_ofile_close_and_next(smb_ofile_t *);
286 +static void smb_ofile_delete(void *arg);
287 +static void smb_ofile_save_dh(void *arg);
288 +
170 289 static int smb_ofile_netinfo_encode(smb_ofile_t *, uint8_t *, size_t,
171 290 uint32_t *);
172 291 static int smb_ofile_netinfo_init(smb_ofile_t *, smb_netfileinfo_t *);
173 292 static void smb_ofile_netinfo_fini(smb_netfileinfo_t *);
174 293
175 294 /*
176 - * smb_ofile_open
295 + * The uniq_fid is a CIFS-server-wide unique identifier for an ofile
296 + * which is used to uniquely identify open instances for the
297 + * VFS share reservation and POSIX locks.
177 298 */
299 +static volatile uint32_t smb_fids = 0;
300 +#define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids)
301 +
302 +/*
303 + * smb_ofile_alloc
304 + * Allocate an ofile and fill in it's "up" pointers, but
305 + * do NOT link it into the tree's list of ofiles or the
306 + * node's list of ofiles. An ofile in this state is a
307 + * "proposed" open passed to the oplock break code.
308 + *
309 + * If we don't get as far as smb_ofile_open with this OF,
310 + * call smb_ofile_free() to free this object.
311 + *
312 + * Note: The following sr members may be null during
313 + * persistent handle import: session, uid_usr, tid_tree
314 + */
178 315 smb_ofile_t *
179 -smb_ofile_open(
316 +smb_ofile_alloc(
180 317 smb_request_t *sr,
181 - smb_node_t *node,
182 - struct open_param *op,
318 + smb_arg_open_t *op,
319 + smb_node_t *node, /* optional (may be NULL) */
183 320 uint16_t ftype,
184 - uint32_t uniqid,
185 - smb_error_t *err)
321 + uint16_t tree_fid)
186 322 {
187 - smb_tree_t *tree = sr->tid_tree;
323 + smb_user_t *user = sr->uid_user; /* optional */
324 + smb_tree_t *tree = sr->tid_tree; /* optional */
188 325 smb_ofile_t *of;
189 - uint16_t fid;
190 - smb_attr_t attr;
191 - int rc;
192 - enum errstates { EMPTY, FIDALLOC, CRHELD, MUTEXINIT };
193 - enum errstates state = EMPTY;
194 326
195 - if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) {
196 - err->status = NT_STATUS_TOO_MANY_OPENED_FILES;
197 - err->errcls = ERRDOS;
198 - err->errcode = ERROR_TOO_MANY_OPEN_FILES;
199 - return (NULL);
200 - }
201 - state = FIDALLOC;
202 -
203 327 of = kmem_cache_alloc(smb_cache_ofile, KM_SLEEP);
204 328 bzero(of, sizeof (smb_ofile_t));
205 329 of->f_magic = SMB_OFILE_MAGIC;
330 +
331 + mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL);
332 + list_create(&of->f_notify.nc_waiters, sizeof (smb_request_t),
333 + offsetof(smb_request_t, sr_waiters));
334 + mutex_init(&of->dh_nvlock, NULL, MUTEX_DEFAULT, NULL);
335 +
336 + of->f_state = SMB_OFILE_STATE_ALLOC;
206 337 of->f_refcnt = 1;
207 - of->f_fid = fid;
208 - of->f_uniqid = uniqid;
338 + of->f_ftype = ftype;
339 + of->f_fid = tree_fid;
340 + /* of->f_persistid see smb2_create */
341 + of->f_uniqid = SMB_UNIQ_FID();
209 342 of->f_opened_by_pid = sr->smb_pid;
210 343 of->f_granted_access = op->desired_access;
211 344 of->f_share_access = op->share_access;
212 345 of->f_create_options = op->create_options;
213 - of->f_cr = (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) ?
214 - smb_user_getprivcred(sr->uid_user) : sr->uid_user->u_cred;
215 - crhold(of->f_cr);
216 - state = CRHELD;
217 - of->f_ftype = ftype;
218 - of->f_server = tree->t_server;
219 - of->f_session = tree->t_session;
346 + if (user != NULL) {
347 + if ((op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) != 0)
348 + of->f_cr = smb_user_getprivcred(user);
349 + else
350 + of->f_cr = user->u_cred;
351 + crhold(of->f_cr);
352 + }
353 + of->f_server = sr->sr_server;
354 + of->f_session = sr->session; /* may be NULL */
355 +
356 + (void) memset(of->f_lock_seq, -1, SMB_OFILE_LSEQ_MAX);
357 +
358 + of->f_mode = smb_fsop_amask_to_omode(of->f_granted_access);
359 + if ((of->f_granted_access & FILE_DATA_ALL) == FILE_EXECUTE)
360 + of->f_flags |= SMB_OFLAGS_EXECONLY;
361 +
220 362 /*
221 - * grab a ref for of->f_user
222 - * released in smb_ofile_delete()
363 + * In case a lease is requested, copy the lease keys now so
364 + * any oplock breaks during open don't break those on our
365 + * other handles that might have the same lease.
223 366 */
224 - smb_user_hold_internal(sr->uid_user);
225 - of->f_user = sr->uid_user;
226 - of->f_tree = tree;
227 - of->f_node = node;
367 + bcopy(op->lease_key, of->TargetOplockKey, SMB_LEASE_KEY_SZ);
368 + bcopy(op->parent_lease_key, of->ParentOplockKey, SMB_LEASE_KEY_SZ);
228 369
229 - mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL);
230 - state = MUTEXINIT;
370 + /*
371 + * grab a ref for of->f_user and of->f_tree
372 + * We know the user and tree must be "live" because
373 + * this SR holds references to them. The node ref. is
374 + * held by our caller, until smb_ofile_open puts this
375 + * ofile on the node ofile list with smb_node_add_ofile.
376 + */
377 + if (user != NULL) {
378 + smb_user_hold_internal(user);
379 + of->f_user = user;
380 + }
381 + if (tree != NULL) {
382 + smb_tree_hold_internal(tree);
383 + of->f_tree = tree;
384 + }
385 + of->f_node = node; /* may be NULL */
386 +
387 + return (of);
388 +}
389 +
390 +/*
391 + * smb_ofile_open
392 + *
393 + * Complete an open on an ofile that was previously allocated by
394 + * smb_ofile_alloc, by putting it on the tree ofile list and
395 + * (if it's a file) the node ofile list.
396 + */
397 +void
398 +smb_ofile_open(
399 + smb_request_t *sr,
400 + smb_arg_open_t *op,
401 + smb_ofile_t *of)
402 +{
403 + smb_tree_t *tree = sr->tid_tree;
404 + smb_node_t *node = of->f_node;
405 +
406 + ASSERT(of->f_state == SMB_OFILE_STATE_ALLOC);
231 407 of->f_state = SMB_OFILE_STATE_OPEN;
232 408
233 - if (ftype == SMB_FTYPE_MESG_PIPE) {
409 + switch (of->f_ftype) {
410 + case SMB_FTYPE_BYTE_PIPE:
411 + case SMB_FTYPE_MESG_PIPE:
234 412 /* See smb_opipe_open. */
235 413 of->f_pipe = op->pipe;
236 414 smb_server_inc_pipes(of->f_server);
237 - } else {
238 - ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */
239 - ASSERT(node);
415 + break;
416 + case SMB_FTYPE_DISK:
417 + case SMB_FTYPE_PRINTER:
418 + /* Regular file, not a pipe */
419 + ASSERT(node != NULL);
240 420
241 - /*
242 - * Note that the common open path often adds bits like
243 - * READ_CONTROL, so the logic "is this open exec-only"
244 - * needs to look at only the FILE_DATA_ALL bits.
245 - */
246 - if ((of->f_granted_access & FILE_DATA_ALL) == FILE_EXECUTE)
247 - of->f_flags |= SMB_OFLAGS_EXECONLY;
248 -
249 - bzero(&attr, sizeof (smb_attr_t));
250 - attr.sa_mask = SMB_AT_UID | SMB_AT_DOSATTR;
251 - rc = smb_node_getattr(NULL, node, of->f_cr, NULL, &attr);
252 - if (rc != 0) {
253 - err->status = NT_STATUS_INTERNAL_ERROR;
254 - err->errcls = ERRDOS;
255 - err->errcode = ERROR_INTERNAL_ERROR;
256 - goto errout;
257 - }
258 - if (crgetuid(of->f_cr) == attr.sa_vattr.va_uid) {
259 - /*
260 - * Add this bit for the file's owner even if it's not
261 - * specified in the request (Windows behavior).
262 - */
263 - of->f_granted_access |= FILE_READ_ATTRIBUTES;
264 - }
265 -
266 - if (smb_node_is_file(node)) {
267 - of->f_mode =
268 - smb_fsop_amask_to_omode(of->f_granted_access);
269 - if (smb_fsop_open(node, of->f_mode, of->f_cr) != 0) {
270 - err->status = NT_STATUS_ACCESS_DENIED;
271 - err->errcls = ERRDOS;
272 - err->errcode = ERROR_ACCESS_DENIED;
273 - goto errout;
274 - }
275 - }
276 -
277 - if (tree->t_flags & SMB_TREE_READONLY)
278 - of->f_flags |= SMB_OFLAGS_READONLY;
279 -
280 - /*
281 - * Note that if we created_readonly, that
282 - * will _not_ yet show in attr.sa_dosattr
283 - * so creating a readonly file gives the
284 - * caller a writable handle as it should.
285 - */
286 - if (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)
287 - of->f_flags |= SMB_OFLAGS_READONLY;
288 -
289 421 smb_node_inc_open_ofiles(node);
290 422 smb_node_add_ofile(node, of);
291 423 smb_node_ref(node);
292 424 smb_server_inc_files(of->f_server);
425 + break;
426 + default:
427 + ASSERT(0);
293 428 }
294 429 smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
295 430 smb_llist_insert_tail(&tree->t_ofile_list, of);
296 431 smb_llist_exit(&tree->t_ofile_list);
297 432 atomic_inc_32(&tree->t_open_files);
298 433 atomic_inc_32(&of->f_session->s_file_cnt);
299 - return (of);
300 434
301 -errout:
302 - switch (state) {
303 - case MUTEXINIT:
304 - mutex_destroy(&of->f_mutex);
305 - smb_user_release(of->f_user);
306 - /*FALLTHROUGH*/
307 - case CRHELD:
308 - crfree(of->f_cr);
309 - of->f_magic = 0;
310 - kmem_cache_free(smb_cache_ofile, of);
311 - /*FALLTHROUGH*/
312 - case FIDALLOC:
313 - smb_idpool_free(&tree->t_fid_pool, fid);
314 - /*FALLTHROUGH*/
315 - case EMPTY:
316 - break;
317 - }
318 - return (NULL);
319 435 }
320 436
321 437 /*
322 438 * smb_ofile_close
439 + *
440 + * Incoming states: (where from)
441 + * SMB_OFILE_STATE_OPEN protocol close, smb_ofile_drop
442 + * SMB_OFILE_STATE_EXPIRED called via smb2_dh_expire
443 + * SMB_OFILE_STATE_ORPHANED smb_server_cleanup_sessions()
323 444 */
324 445 void
325 446 smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
326 447 {
327 448 smb_attr_t *pa;
328 449 timestruc_t now;
329 - uint32_t flags = 0;
330 450
331 451 SMB_OFILE_VALID(of);
332 452
333 453 mutex_enter(&of->f_mutex);
334 454 ASSERT(of->f_refcnt);
335 - if (of->f_state != SMB_OFILE_STATE_OPEN) {
455 +
456 + switch (of->f_state) {
457 + case SMB_OFILE_STATE_OPEN:
458 + case SMB_OFILE_STATE_ORPHANED:
459 + case SMB_OFILE_STATE_EXPIRED:
460 + of->f_state = SMB_OFILE_STATE_CLOSING;
336 461 mutex_exit(&of->f_mutex);
462 + break;
463 + default:
464 + mutex_exit(&of->f_mutex);
337 465 return;
338 466 }
339 - of->f_state = SMB_OFILE_STATE_CLOSING;
340 - mutex_exit(&of->f_mutex);
341 467
468 + /*
469 + * Only one thread here (the one that that set f_state closing)
470 + */
342 471 switch (of->f_ftype) {
343 472 case SMB_FTYPE_BYTE_PIPE:
344 473 case SMB_FTYPE_MESG_PIPE:
345 474 smb_opipe_close(of);
346 475 smb_server_dec_pipes(of->f_server);
347 476 break;
348 477
349 478 case SMB_FTYPE_DISK:
350 - case SMB_FTYPE_PRINTER:
479 + if (of->dh_persist)
480 + smb2_dh_close_persistent(of);
481 + if (of->f_persistid != 0)
482 + smb_ofile_del_persistid(of);
483 + if (of->f_lease != NULL)
484 + smb2_lease_ofile_close(of);
485 + smb_oplock_break_CLOSE(of->f_node, of);
486 + /* FALLTHROUGH */
487 +
488 + case SMB_FTYPE_PRINTER: /* or FTYPE_DISK */
351 489 /*
352 490 * In here we make changes to of->f_pending_attr
353 491 * while not holding of->f_mutex. This is OK
354 492 * because we've changed f_state to CLOSING,
355 493 * so no more threads will take this path.
356 494 */
357 495 pa = &of->f_pending_attr;
358 496 if (mtime_sec != 0) {
359 497 pa->sa_vattr.va_mtime.tv_sec = mtime_sec;
360 498 pa->sa_mask |= SMB_AT_MTIME;
361 499 }
362 500
363 501 /*
364 502 * If we have ever modified data via this handle
365 503 * (write or truncate) and if the mtime was not
366 504 * set via this handle, update the mtime again
367 505 * during the close. Windows expects this.
|
↓ open down ↓ |
7 lines elided |
↑ open up ↑ |
368 506 * [ MS-FSA 2.1.5.4 "Update Timestamps" ]
369 507 */
370 508 if (of->f_written &&
371 509 (pa->sa_mask & SMB_AT_MTIME) == 0) {
372 510 pa->sa_mask |= SMB_AT_MTIME;
373 511 gethrestime(&now);
374 512 pa->sa_vattr.va_mtime = now;
375 513 }
376 514
377 515 if (of->f_flags & SMB_OFLAGS_SET_DELETE_ON_CLOSE) {
378 - if (smb_tree_has_feature(of->f_tree,
379 - SMB_TREE_CATIA)) {
380 - flags |= SMB_CATIA;
381 - }
516 + /* We delete using the on-disk name. */
517 + uint32_t flags = SMB_CASE_SENSITIVE;
382 518 (void) smb_node_set_delete_on_close(of->f_node,
383 519 of->f_cr, flags);
384 520 }
385 521 smb_fsop_unshrlock(of->f_cr, of->f_node, of->f_uniqid);
386 522 smb_node_destroy_lock_by_ofile(of->f_node, of);
387 523
388 524 if (smb_node_is_file(of->f_node)) {
389 525 (void) smb_fsop_close(of->f_node, of->f_mode,
390 526 of->f_cr);
391 - smb_oplock_release(of->f_node, of);
392 527 } else {
393 528 /*
394 529 * If there was an odir, close it.
395 530 */
396 531 if (of->f_odir != NULL)
397 532 smb_odir_close(of->f_odir);
533 + /*
534 + * Cancel any notify change requests that
535 + * might be watching this open file (dir),
536 + * and unsubscribe it from node events.
537 + *
538 + * Can't hold f_mutex when calling smb_notify_ofile.
539 + * Don't really need it when unsubscribing, but
540 + * harmless, and consistent with subscribing.
541 + */
542 + if (of->f_notify.nc_subscribed)
543 + smb_notify_ofile(of,
544 + FILE_ACTION_HANDLE_CLOSED, NULL);
545 + mutex_enter(&of->f_mutex);
546 + if (of->f_notify.nc_subscribed) {
547 + of->f_notify.nc_subscribed = B_FALSE;
548 + smb_node_fcn_unsubscribe(of->f_node);
549 + of->f_notify.nc_filter = 0;
550 + }
551 + mutex_exit(&of->f_mutex);
398 552 }
399 553 if (smb_node_dec_open_ofiles(of->f_node) == 0) {
400 554 /*
401 - * Last close. The f_pending_attr has
402 - * only times (atime,ctime,mtime) so
403 - * we can borrow it to commit the
404 - * n_pending_dosattr from the node.
555 + * Last close. If we're not deleting
556 + * the file, apply any pending attrs.
557 + * Leave allocsz zero when no open files,
558 + * just to avoid confusion, because it's
559 + * only updated when there are opens.
560 + * XXX: Just do this on _every_ close.
405 561 */
406 - pa->sa_dosattr =
407 - of->f_node->n_pending_dosattr;
408 - if (pa->sa_dosattr != 0)
409 - pa->sa_mask |= SMB_AT_DOSATTR;
410 - /* Let's leave this zero when not in use. */
562 + mutex_enter(&of->f_node->n_mutex);
563 + if (of->f_node->flags & NODE_FLAGS_DELETE_ON_CLOSE) {
564 + smb_node_delete_on_close(of->f_node);
565 + pa->sa_mask = 0;
566 + }
411 567 of->f_node->n_allocsz = 0;
568 + mutex_exit(&of->f_node->n_mutex);
412 569 }
413 570 if (pa->sa_mask != 0) {
414 571 /*
415 572 * Commit any pending attributes from
416 573 * the ofile we're closing. Note that
417 574 * we pass NULL as the ofile to setattr
418 575 * so it will write to the file system
419 576 * and not keep anything on the ofile.
420 - * This clears n_pending_dosattr if
421 - * there are no opens, otherwise the
422 - * dosattr will be pending again.
423 577 */
424 578 (void) smb_node_setattr(NULL, of->f_node,
425 579 of->f_cr, NULL, pa);
426 580 }
427 581
428 - /*
429 - * Cancel any notify change requests that
430 - * may be using this open instance.
431 - */
432 - if (of->f_node->n_fcn.fcn_count)
433 - smb_notify_file_closed(of);
434 -
435 582 smb_server_dec_files(of->f_server);
436 583 break;
437 584 }
438 - atomic_dec_32(&of->f_tree->t_open_files);
439 585
440 - mutex_enter(&of->f_mutex);
441 - ASSERT(of->f_refcnt);
442 - ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING);
443 - of->f_state = SMB_OFILE_STATE_CLOSED;
444 - mutex_exit(&of->f_mutex);
586 + /*
587 + * Keep f_state == SMB_OFILE_STATE_CLOSING
588 + * until the last ref. is dropped, in
589 + * smb_ofile_release()
590 + */
445 591 }
446 592
447 593 /*
448 - * smb_ofile_close_all
594 + * "Destructor" function for smb_ofile_close_all, and
595 + * smb_ofile_close_all_by_pid, called after the llist lock
596 + * for tree list has been exited. Our job is to either
597 + * close this ofile, or (if durable) set state _SAVE_DH.
449 598 *
599 + * The next interesting thing happens when the last ref.
600 + * on this ofile calls smb_ofile_release(), where we
601 + * eihter delete the ofile, or (if durable) leave it
602 + * in the persistid hash table for possible reclaim.
450 603 *
604 + * This is run via smb_llist_post (after smb_llist_exit)
605 + * because smb_ofile_close can block, and we'd rather not
606 + * block while holding the ofile list as reader.
451 607 */
452 -void
453 -smb_ofile_close_all(
454 - smb_tree_t *tree)
608 +static void
609 +smb_ofile_drop(void *arg)
455 610 {
456 - smb_ofile_t *of;
611 + smb_ofile_t *of = arg;
457 612
458 - ASSERT(tree);
459 - ASSERT(tree->t_magic == SMB_TREE_MAGIC);
613 + mutex_enter(&of->f_mutex);
614 + switch (of->f_state) {
615 + case SMB_OFILE_STATE_OPEN:
616 + /* DH checks under mutex. */
617 + if (of->f_ftype == SMB_FTYPE_DISK &&
618 + of->dh_vers != SMB2_NOT_DURABLE &&
619 + smb_dh_should_save(of)) {
620 + /*
621 + * Tell smb_ofile_release() to
622 + * make this an _ORPHANED DH.
623 + */
624 + of->f_state = SMB_OFILE_STATE_SAVE_DH;
625 + mutex_exit(&of->f_mutex);
626 + break;
627 + }
628 + /* OK close it. */
629 + mutex_exit(&of->f_mutex);
630 + smb_ofile_close(of, 0);
631 + break;
460 632
461 - smb_llist_enter(&tree->t_ofile_list, RW_READER);
462 - of = smb_llist_head(&tree->t_ofile_list);
463 - while (of) {
464 - ASSERT(of->f_magic == SMB_OFILE_MAGIC);
465 - ASSERT(of->f_tree == tree);
466 - of = smb_ofile_close_and_next(of);
633 + default:
634 + /* Something else closed it already. */
635 + mutex_exit(&of->f_mutex);
636 + break;
467 637 }
468 - smb_llist_exit(&tree->t_ofile_list);
638 +
639 + /*
640 + * Release the ref acquired during the traversal loop.
641 + * Note that on the last ref, this ofile will be
642 + * removed from the tree list etc.
643 + * See: smb_llist_post, smb_ofile_delete
644 + */
645 + smb_ofile_release(of);
469 646 }
470 647
471 648 /*
472 - * smb_ofiles_close_by_pid
649 + * smb_ofile_close_all
473 650 *
474 651 *
475 652 */
476 653 void
477 -smb_ofile_close_all_by_pid(
654 +smb_ofile_close_all(
478 655 smb_tree_t *tree,
479 - uint16_t pid)
656 + uint32_t pid)
480 657 {
481 658 smb_ofile_t *of;
659 + smb_llist_t *ll;
482 660
483 661 ASSERT(tree);
484 662 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
485 663
486 - smb_llist_enter(&tree->t_ofile_list, RW_READER);
487 - of = smb_llist_head(&tree->t_ofile_list);
488 - while (of) {
664 + ll = &tree->t_ofile_list;
665 +
666 + smb_llist_enter(ll, RW_READER);
667 + for (of = smb_llist_head(ll);
668 + of != NULL;
669 + of = smb_llist_next(ll, of)) {
489 670 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
490 671 ASSERT(of->f_tree == tree);
491 - if (of->f_opened_by_pid == pid) {
492 - of = smb_ofile_close_and_next(of);
493 - } else {
494 - of = smb_llist_next(&tree->t_ofile_list, of);
672 + if (pid != 0 && of->f_opened_by_pid != pid)
673 + continue;
674 + if (smb_ofile_hold(of)) {
675 + smb_llist_post(ll, of, smb_ofile_drop);
495 676 }
496 677 }
497 - smb_llist_exit(&tree->t_ofile_list);
678 +
679 + /*
680 + * Drop the lock and process the llist dtor queue.
681 + * Calls smb_ofile_drop on ofiles that were open.
682 + */
683 + smb_llist_exit(ll);
498 684 }
499 685
500 686 /*
501 687 * If the enumeration request is for ofile data, handle it here.
502 688 * Otherwise, return.
503 689 *
504 690 * This function should be called with a hold on the ofile.
505 691 */
506 692 int
507 693 smb_ofile_enum(smb_ofile_t *of, smb_svcenum_t *svcenum)
508 694 {
509 695 uint8_t *pb;
510 696 uint_t nbytes;
511 697 int rc;
512 698
513 699 ASSERT(of);
514 700 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
515 701 ASSERT(of->f_refcnt);
516 702
517 703 if (svcenum->se_type != SMB_SVCENUM_TYPE_FILE)
518 704 return (0);
519 705
520 706 if (svcenum->se_nskip > 0) {
521 707 svcenum->se_nskip--;
522 708 return (0);
523 709 }
524 710
525 711 if (svcenum->se_nitems >= svcenum->se_nlimit) {
526 712 svcenum->se_nitems = svcenum->se_nlimit;
527 713 return (0);
528 714 }
529 715
530 716 pb = &svcenum->se_buf[svcenum->se_bused];
531 717
532 718 rc = smb_ofile_netinfo_encode(of, pb, svcenum->se_bavail,
533 719 &nbytes);
|
↓ open down ↓ |
26 lines elided |
↑ open up ↑ |
534 720 if (rc == 0) {
535 721 svcenum->se_bavail -= nbytes;
536 722 svcenum->se_bused += nbytes;
537 723 svcenum->se_nitems++;
538 724 }
539 725
540 726 return (rc);
541 727 }
542 728
543 729 /*
730 + * Take a reference on an open file, in any of the states:
731 + * RECONNECT, SAVE_DH, OPEN, ORPHANED.
732 + * Return TRUE if ref taken. Used for oplock breaks.
733 + *
734 + * Note: When the oplock break code calls this, it holds the
735 + * node ofile list lock and node oplock mutex. When we see
736 + * an ofile in states RECONNECT or SAVING, we know the ofile
737 + * is gaining or losing it's tree, and that happens quickly,
738 + * so we just wait for that work to finish. However, the
739 + * waiting for state transitions here means we have to be
740 + * careful not to re-enter the node list lock or otherwise
741 + * block on things that could cause a deadlock. Waiting
742 + * just on of->f_mutex here is OK.
743 + */
744 +boolean_t
745 +smb_ofile_hold_olbrk(smb_ofile_t *of)
746 +{
747 + boolean_t ret = B_FALSE;
748 +
749 + ASSERT(of);
750 + ASSERT(of->f_magic == SMB_OFILE_MAGIC);
751 +
752 + mutex_enter(&of->f_mutex);
753 +
754 +again:
755 + switch (of->f_state) {
756 + case SMB_OFILE_STATE_RECONNECT:
757 + case SMB_OFILE_STATE_SAVING:
758 + cv_wait(&of->f_cv, &of->f_mutex);
759 + goto again;
760 +
761 + case SMB_OFILE_STATE_OPEN:
762 + case SMB_OFILE_STATE_ORPHANED:
763 + case SMB_OFILE_STATE_SAVE_DH:
764 + of->f_refcnt++;
765 + ret = B_TRUE;
766 + break;
767 +
768 + default:
769 + break;
770 + }
771 + mutex_exit(&of->f_mutex);
772 +
773 + return (ret);
774 +}
775 +
776 +/*
544 777 * Take a reference on an open file.
545 778 */
546 779 boolean_t
547 780 smb_ofile_hold(smb_ofile_t *of)
548 781 {
549 782 ASSERT(of);
550 783 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
551 784
552 785 mutex_enter(&of->f_mutex);
553 786
554 787 if (of->f_state != SMB_OFILE_STATE_OPEN) {
555 788 mutex_exit(&of->f_mutex);
556 789 return (B_FALSE);
557 790 }
558 791 of->f_refcnt++;
|
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
559 792
560 793 mutex_exit(&of->f_mutex);
561 794 return (B_TRUE);
562 795 }
563 796
564 797 /*
565 798 * Release a reference on a file. If the reference count falls to
566 799 * zero and the file has been closed, post the object for deletion.
567 800 * Object deletion is deferred to avoid modifying a list while an
568 801 * iteration may be in progress.
802 + *
803 + * We're careful to avoid dropping f_session etc. until the last
804 + * reference goes away. The oplock break code depends on that
805 + * not changing while it holds a ref. on an ofile.
569 806 */
570 807 void
571 808 smb_ofile_release(smb_ofile_t *of)
572 809 {
810 + smb_tree_t *tree = of->f_tree;
811 + boolean_t delete = B_FALSE;
812 +
573 813 SMB_OFILE_VALID(of);
574 814
575 815 mutex_enter(&of->f_mutex);
576 - ASSERT(of->f_refcnt);
816 + ASSERT(of->f_refcnt > 0);
577 817 of->f_refcnt--;
818 +
578 819 switch (of->f_state) {
579 820 case SMB_OFILE_STATE_OPEN:
580 - case SMB_OFILE_STATE_CLOSING:
821 + case SMB_OFILE_STATE_ORPHANED:
822 + case SMB_OFILE_STATE_EXPIRED:
581 823 break;
582 824
583 - case SMB_OFILE_STATE_CLOSED:
584 - if (of->f_refcnt == 0)
585 - smb_tree_post_ofile(of->f_tree, of);
825 + case SMB_OFILE_STATE_SAVE_DH:
826 + ASSERT(tree != NULL);
827 + if (of->f_refcnt == 0) {
828 + of->f_state = SMB_OFILE_STATE_SAVING;
829 + smb_llist_post(&tree->t_ofile_list, of,
830 + smb_ofile_save_dh);
831 + }
586 832 break;
587 833
834 + case SMB_OFILE_STATE_CLOSING:
835 + /* Note, tree == NULL on _ORPHANED */
836 + if (of->f_refcnt == 0) {
837 + of->f_state = SMB_OFILE_STATE_CLOSED;
838 + if (tree == NULL) {
839 + /* Skip smb_llist_post */
840 + delete = B_TRUE;
841 + break;
842 + }
843 + smb_llist_post(&tree->t_ofile_list, of,
844 + smb_ofile_delete);
845 + }
846 + break;
847 +
588 848 default:
589 849 ASSERT(0);
590 850 break;
591 851 }
592 852 mutex_exit(&of->f_mutex);
593 -}
594 853
595 -/*
596 - * smb_ofile_request_complete
597 - *
598 - * During oplock acquisition, all other oplock requests on the node
599 - * are blocked until the acquire request completes and the response
600 - * is on the wire.
601 - * Call smb_oplock_broadcast to notify the node that the request
602 - * has completed.
603 - *
604 - * THIS MECHANISM RELIES ON THE FACT THAT THE OFILE IS NOT REMOVED
605 - * FROM THE SR UNTIL REQUEST COMPLETION (when the sr is destroyed)
606 - */
607 -void
608 -smb_ofile_request_complete(smb_ofile_t *of)
609 -{
610 - SMB_OFILE_VALID(of);
611 -
612 - switch (of->f_ftype) {
613 - case SMB_FTYPE_DISK:
614 - ASSERT(of->f_node);
615 - smb_oplock_broadcast(of->f_node);
616 - break;
617 - case SMB_FTYPE_MESG_PIPE:
618 - break;
619 - default:
620 - break;
854 + /*
855 + * When we drop the last ref. on an expired DH, it's no longer
856 + * in any tree, so skip the smb_llist_post and just call
857 + * smb_ofile_delete directly.
858 + */
859 + if (delete) {
860 + smb_ofile_delete(of);
621 861 }
622 862 }
623 863
624 864 /*
625 865 * smb_ofile_lookup_by_fid
626 866 *
627 867 * Find the open file whose fid matches the one specified in the request.
628 868 * If we can't find the fid or the shares (trees) don't match, we have a
629 869 * bad fid.
630 870 */
631 871 smb_ofile_t *
632 872 smb_ofile_lookup_by_fid(
633 873 smb_request_t *sr,
634 874 uint16_t fid)
635 875 {
636 876 smb_tree_t *tree = sr->tid_tree;
637 877 smb_llist_t *of_list;
638 878 smb_ofile_t *of;
639 879
640 880 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
641 881
642 882 of_list = &tree->t_ofile_list;
643 883
644 884 smb_llist_enter(of_list, RW_READER);
645 885 of = smb_llist_head(of_list);
646 886 while (of) {
647 887 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
648 888 ASSERT(of->f_tree == tree);
649 889 if (of->f_fid == fid)
650 890 break;
651 891 of = smb_llist_next(of_list, of);
652 892 }
653 893 if (of == NULL)
654 894 goto out;
|
↓ open down ↓ |
24 lines elided |
↑ open up ↑ |
655 895
656 896 /*
657 897 * Only allow use of a given FID with the same UID that
658 898 * was used to open it. MS-CIFS 3.3.5.14
659 899 */
660 900 if (of->f_user != sr->uid_user) {
661 901 of = NULL;
662 902 goto out;
663 903 }
664 904
905 + /* inline smb_ofile_hold() */
665 906 mutex_enter(&of->f_mutex);
666 907 if (of->f_state != SMB_OFILE_STATE_OPEN) {
667 908 mutex_exit(&of->f_mutex);
668 909 of = NULL;
669 910 goto out;
670 911 }
671 912 of->f_refcnt++;
672 913 mutex_exit(&of->f_mutex);
673 914
674 915 out:
675 916 smb_llist_exit(of_list);
676 917 return (of);
677 918 }
678 919
679 920 /*
680 921 * smb_ofile_lookup_by_uniqid
681 922 *
682 923 * Find the open file whose uniqid matches the one specified in the request.
683 924 */
684 925 smb_ofile_t *
685 926 smb_ofile_lookup_by_uniqid(smb_tree_t *tree, uint32_t uniqid)
686 927 {
687 928 smb_llist_t *of_list;
688 929 smb_ofile_t *of;
689 930
690 931 ASSERT(tree->t_magic == SMB_TREE_MAGIC);
691 932
692 933 of_list = &tree->t_ofile_list;
693 934 smb_llist_enter(of_list, RW_READER);
694 935 of = smb_llist_head(of_list);
695 936
696 937 while (of) {
697 938 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
698 939 ASSERT(of->f_tree == tree);
699 940
700 941 if (of->f_uniqid == uniqid) {
701 942 if (smb_ofile_hold(of)) {
702 943 smb_llist_exit(of_list);
703 944 return (of);
704 945 }
|
↓ open down ↓ |
30 lines elided |
↑ open up ↑ |
705 946 }
706 947
707 948 of = smb_llist_next(of_list, of);
708 949 }
709 950
710 951 smb_llist_exit(of_list);
711 952 return (NULL);
712 953 }
713 954
714 955 /*
956 + * Durable ID (or persistent ID)
957 + */
958 +
959 +static smb_ofile_t *
960 +smb_ofile_hold_cb(smb_ofile_t *of)
961 +{
962 + smb_ofile_t *ret = of;
963 +
964 + mutex_enter(&of->f_mutex);
965 + if (of->f_state == SMB_OFILE_STATE_ORPHANED)
966 + /* inline smb_ofile_hold() */
967 + of->f_refcnt++;
968 + else
969 + ret = NULL;
970 +
971 + mutex_exit(&of->f_mutex);
972 + return (ret);
973 +}
974 +
975 +/*
976 + * Lookup an ofile by persistent ID, and return ONLY if in state ORPHANED
977 + * This is used by SMB2 create "reclaim".
978 + */
979 +smb_ofile_t *
980 +smb_ofile_lookup_by_persistid(smb_request_t *sr, uint64_t persistid)
981 +{
982 + smb_hash_t *hash;
983 + smb_bucket_t *bucket;
984 + smb_llist_t *ll;
985 + smb_ofile_t *of;
986 + uint_t idx;
987 +
988 + if (persistid == 0)
989 + return (NULL);
990 +
991 + hash = sr->sr_server->sv_persistid_ht;
992 + idx = smb_hash_uint64(hash, persistid);
993 + bucket = &hash->buckets[idx];
994 + ll = &bucket->b_list;
995 +
996 + smb_llist_enter(ll, RW_READER);
997 + of = smb_llist_head(ll);
998 + while (of != NULL) {
999 + if (of->f_persistid == persistid)
1000 + break;
1001 + of = smb_llist_next(ll, of);
1002 + }
1003 + if (of != NULL)
1004 + of = smb_ofile_hold_cb(of);
1005 + smb_llist_exit(ll);
1006 +
1007 + return (of);
1008 +}
1009 +
1010 +/*
1011 + * Create a (unique) durable/persistent ID for a new ofile,
1012 + * and add this ofile to the persistid hash table. This ID
1013 + * is referred to as the persistent ID in the protocol spec,
1014 + * so that's what we call it too, though the persistence may
1015 + * vary. "Durable" handles are persistent across reconnects
1016 + * but not server reboots. Persistent handles are persistent
1017 + * across server reboots too.
1018 + *
1019 + * Note that persistent IDs need to be unique for the lifetime of
1020 + * any given ofile. For normal (non-persistent) ofiles we can just
1021 + * use a persistent ID derived from the ofile memory address, as
1022 + * these don't ever live beyond the current OS boot lifetime.
1023 + *
1024 + * Persistent handles are re-imported after server restart, and
1025 + * generally have a different memory address after import than
1026 + * they had in the previous OS boot lifetime, so for these we
1027 + * use a randomly assigned value that won't conflict with any
1028 + * non-persistent (durable) handles. Ensuring that a randomly
1029 + * generated ID is unique requres a search of the ofiles in one
1030 + * hash bucket, which we'd rather avoid for non-persistent opens.
1031 + *
1032 + * The solution used here is to divide the persistent ID space
1033 + * in half (odd and even values) where durable opens use an ID
1034 + * derived from the ofile address (which is always even), and
1035 + * persistent opens use an ID generated randomly (always odd).
1036 + *
1037 + * smb_ofile_set_persistid_dh() sets a durable handle ID and
1038 + * smb_ofile_set_persistid_ph() sets a persistent handle ID.
1039 + */
1040 +void
1041 +smb_ofile_set_persistid_dh(smb_ofile_t *of)
1042 +{
1043 + smb_hash_t *hash = of->f_server->sv_persistid_ht;
1044 + smb_bucket_t *bucket;
1045 + smb_llist_t *ll;
1046 + uint64_t persistid;
1047 + uint_t idx;
1048 +
1049 + persistid = (uintptr_t)of;
1050 + /* Avoid showing object addresses */
1051 + persistid ^= ((uintptr_t)&smb_cache_ofile);
1052 + /* make sure it's even */
1053 + persistid &= ~((uint64_t)1);
1054 +
1055 + idx = smb_hash_uint64(hash, persistid);
1056 + bucket = &hash->buckets[idx];
1057 + ll = &bucket->b_list;
1058 + smb_llist_enter(ll, RW_WRITER);
1059 + if (of->f_persistid == 0) {
1060 + of->f_persistid = persistid;
1061 + smb_llist_insert_tail(ll, of);
1062 + }
1063 + smb_llist_exit(ll);
1064 +}
1065 +
1066 +void
1067 +smb_ofile_set_persistid_ph(smb_ofile_t *of)
1068 +{
1069 + uint64_t persistid;
1070 + int rc;
1071 +
1072 +top:
1073 + (void) random_get_pseudo_bytes((uint8_t *)&persistid,
1074 + sizeof (persistid));
1075 + if (persistid == 0) {
1076 + cmn_err(CE_NOTE, "random gave all zeros!");
1077 + goto top;
1078 + }
1079 + /* make sure it's odd */
1080 + persistid |= (uint64_t)1;
1081 +
1082 + /*
1083 + * Try inserting with this persistent ID.
1084 + */
1085 + rc = smb_ofile_insert_persistid(of, persistid);
1086 + if (rc == EEXIST)
1087 + goto top;
1088 + if (rc != 0) {
1089 + cmn_err(CE_NOTE, "set persistid rc=%d", rc);
1090 + }
1091 +}
1092 +
1093 +/*
1094 + * Insert an ofile into the persistid hash table.
1095 + * If the persistent ID is in use, error.
1096 + */
1097 +int
1098 +smb_ofile_insert_persistid(smb_ofile_t *new_of, uint64_t persistid)
1099 +{
1100 + smb_hash_t *hash = new_of->f_server->sv_persistid_ht;
1101 + smb_bucket_t *bucket;
1102 + smb_llist_t *ll;
1103 + smb_ofile_t *of;
1104 + uint_t idx;
1105 +
1106 + ASSERT(persistid != 0);
1107 +
1108 + /*
1109 + * Look to see if this key alreay exists.
1110 + */
1111 + idx = smb_hash_uint64(hash, persistid);
1112 + bucket = &hash->buckets[idx];
1113 + ll = &bucket->b_list;
1114 +
1115 + smb_llist_enter(ll, RW_WRITER);
1116 + of = smb_llist_head(ll);
1117 + while (of != NULL) {
1118 + if (of->f_persistid == persistid) {
1119 + /* already in use */
1120 + smb_llist_exit(ll);
1121 + return (EEXIST);
1122 + }
1123 + of = smb_llist_next(ll, of);
1124 + }
1125 +
1126 + /* Not found, so OK to insert. */
1127 + if (new_of->f_persistid == 0) {
1128 + new_of->f_persistid = persistid;
1129 + smb_llist_insert_tail(ll, new_of);
1130 + }
1131 + smb_llist_exit(ll);
1132 +
1133 + return (0);
1134 +}
1135 +
1136 +void
1137 +smb_ofile_del_persistid(smb_ofile_t *of)
1138 +{
1139 + smb_hash_t *hash = of->f_server->sv_persistid_ht;
1140 + smb_bucket_t *bucket;
1141 + smb_llist_t *ll;
1142 + uint_t idx;
1143 +
1144 + idx = smb_hash_uint64(hash, of->f_persistid);
1145 + bucket = &hash->buckets[idx];
1146 + ll = &bucket->b_list;
1147 + smb_llist_enter(ll, RW_WRITER);
1148 + if (of->f_persistid != 0) {
1149 + smb_llist_remove(ll, of);
1150 + of->f_persistid = 0;
1151 + }
1152 + smb_llist_exit(ll);
1153 +}
1154 +
1155 +
1156 +/*
715 1157 * Disallow NetFileClose on certain ofiles to avoid side-effects.
716 1158 * Closing a tree root is not allowed: use NetSessionDel or NetShareDel.
717 1159 * Closing SRVSVC connections is not allowed because this NetFileClose
718 1160 * request may depend on this ofile.
719 1161 */
720 1162 boolean_t
721 1163 smb_ofile_disallow_fclose(smb_ofile_t *of)
722 1164 {
723 1165 ASSERT(of);
724 1166 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
725 1167 ASSERT(of->f_refcnt);
726 1168
727 1169 switch (of->f_ftype) {
728 1170 case SMB_FTYPE_DISK:
729 1171 ASSERT(of->f_tree);
730 1172 return (of->f_node == of->f_tree->t_snode);
731 1173
732 1174 case SMB_FTYPE_MESG_PIPE:
733 1175 ASSERT(of->f_pipe);
734 1176 if (smb_strcasecmp(of->f_pipe->p_name, "SRVSVC", 0) == 0)
735 1177 return (B_TRUE);
736 1178 break;
737 1179 default:
738 1180 break;
739 1181 }
740 1182
741 1183 return (B_FALSE);
742 1184 }
743 1185
744 1186 /*
745 1187 * smb_ofile_set_flags
746 1188 *
747 1189 * Return value:
748 1190 *
749 1191 * Current flags value
750 1192 *
751 1193 */
752 1194 void
753 1195 smb_ofile_set_flags(
754 1196 smb_ofile_t *of,
755 1197 uint32_t flags)
756 1198 {
757 1199 ASSERT(of);
758 1200 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
759 1201 ASSERT(of->f_refcnt);
760 1202
761 1203 mutex_enter(&of->f_mutex);
762 1204 of->f_flags |= flags;
763 1205 mutex_exit(&of->f_mutex);
764 1206 }
765 1207
766 1208 /*
767 1209 * smb_ofile_seek
768 1210 *
769 1211 * Return value:
770 1212 *
771 1213 * 0 Success
772 1214 * EINVAL Unknown mode
773 1215 * EOVERFLOW offset too big
774 1216 *
775 1217 */
776 1218 int
777 1219 smb_ofile_seek(
778 1220 smb_ofile_t *of,
779 1221 ushort_t mode,
780 1222 int32_t off,
781 1223 uint32_t *retoff)
782 1224 {
783 1225 u_offset_t newoff = 0;
784 1226 int rc = 0;
785 1227 smb_attr_t attr;
786 1228
787 1229 ASSERT(of);
788 1230 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
789 1231 ASSERT(of->f_refcnt);
790 1232
791 1233 mutex_enter(&of->f_mutex);
792 1234 switch (mode) {
793 1235 case SMB_SEEK_SET:
794 1236 if (off < 0)
795 1237 newoff = 0;
796 1238 else
797 1239 newoff = (u_offset_t)off;
798 1240 break;
799 1241
800 1242 case SMB_SEEK_CUR:
801 1243 if (off < 0 && (-off) > of->f_seek_pos)
802 1244 newoff = 0;
803 1245 else
804 1246 newoff = of->f_seek_pos + (u_offset_t)off;
805 1247 break;
806 1248
807 1249 case SMB_SEEK_END:
808 1250 bzero(&attr, sizeof (smb_attr_t));
809 1251 attr.sa_mask |= SMB_AT_SIZE;
810 1252 rc = smb_fsop_getattr(NULL, zone_kcred(), of->f_node, &attr);
811 1253 if (rc != 0) {
812 1254 mutex_exit(&of->f_mutex);
813 1255 return (rc);
814 1256 }
815 1257 if (off < 0 && (-off) > attr.sa_vattr.va_size)
816 1258 newoff = 0;
817 1259 else
818 1260 newoff = attr.sa_vattr.va_size + (u_offset_t)off;
819 1261 break;
820 1262
821 1263 default:
822 1264 mutex_exit(&of->f_mutex);
823 1265 return (EINVAL);
824 1266 }
825 1267
826 1268 /*
827 1269 * See comments at the beginning of smb_seek.c.
828 1270 * If the offset is greater than UINT_MAX, we will return an error.
829 1271 */
830 1272
831 1273 if (newoff > UINT_MAX) {
832 1274 rc = EOVERFLOW;
833 1275 } else {
834 1276 of->f_seek_pos = newoff;
835 1277 *retoff = (uint32_t)newoff;
836 1278 }
837 1279 mutex_exit(&of->f_mutex);
838 1280 return (rc);
839 1281 }
840 1282
841 1283 /*
842 1284 * smb_ofile_flush
843 1285 *
844 1286 * If writes on this file are not synchronous, flush it using the NFSv3
845 1287 * commit interface.
846 1288 *
847 1289 * XXX - todo: Flush named pipe should drain writes.
848 1290 */
849 1291 void
850 1292 smb_ofile_flush(struct smb_request *sr, struct smb_ofile *of)
851 1293 {
852 1294 switch (of->f_ftype) {
853 1295 case SMB_FTYPE_DISK:
854 1296 if ((of->f_node->flags & NODE_FLAGS_WRITE_THROUGH) == 0)
855 1297 (void) smb_fsop_commit(sr, of->f_cr, of->f_node);
856 1298 break;
857 1299 default:
858 1300 break;
859 1301 }
860 1302 }
861 1303
862 1304 /*
863 1305 * smb_ofile_is_open
864 1306 */
865 1307 boolean_t
866 1308 smb_ofile_is_open(smb_ofile_t *of)
867 1309 {
868 1310 boolean_t rc;
869 1311
870 1312 SMB_OFILE_VALID(of);
871 1313
872 1314 mutex_enter(&of->f_mutex);
873 1315 rc = smb_ofile_is_open_locked(of);
874 1316 mutex_exit(&of->f_mutex);
875 1317 return (rc);
876 1318 }
|
↓ open down ↓ |
152 lines elided |
↑ open up ↑ |
877 1319
878 1320 /* *************************** Static Functions ***************************** */
879 1321
880 1322 /*
881 1323 * Determine whether or not an ofile is open.
882 1324 * This function must be called with the mutex held.
883 1325 */
884 1326 static boolean_t
885 1327 smb_ofile_is_open_locked(smb_ofile_t *of)
886 1328 {
1329 + ASSERT(MUTEX_HELD(&of->f_mutex));
1330 +
887 1331 switch (of->f_state) {
888 1332 case SMB_OFILE_STATE_OPEN:
1333 + case SMB_OFILE_STATE_SAVE_DH:
1334 + case SMB_OFILE_STATE_SAVING:
1335 + case SMB_OFILE_STATE_ORPHANED:
1336 + case SMB_OFILE_STATE_RECONNECT:
889 1337 return (B_TRUE);
890 1338
891 1339 case SMB_OFILE_STATE_CLOSING:
892 1340 case SMB_OFILE_STATE_CLOSED:
1341 + case SMB_OFILE_STATE_EXPIRED:
893 1342 return (B_FALSE);
894 1343
895 1344 default:
896 1345 ASSERT(0);
897 1346 return (B_FALSE);
898 1347 }
899 1348 }
900 1349
901 1350 /*
902 - * This function closes the file passed in (if appropriate) and returns the
903 - * next open file in the list of open files of the tree of the open file passed
904 - * in. It requires that the list of open files of the tree be entered in
905 - * RW_READER mode before being called.
1351 + * smb_ofile_save_dh
1352 + *
1353 + * Called via smb_llist_post (after smb_llist_exit) when the last ref.
1354 + * on this ofile has gone, and this ofile is a "durable handle" (DH)
1355 + * that has state we've decided to save.
1356 + *
1357 + * This does parts of what smb_ofile_delete would do, including:
1358 + * remove the ofile from the tree ofile list and related.
1359 + *
1360 + * We leave the ofile in state ORPHANED, ready for reconnect
1361 + * or expiration via smb2_dh_expire (see smb_ofile_delete).
906 1362 */
907 -static smb_ofile_t *
908 -smb_ofile_close_and_next(smb_ofile_t *of)
1363 +static void
1364 +smb_ofile_save_dh(void *arg)
909 1365 {
910 - smb_ofile_t *next_of;
911 - smb_tree_t *tree;
1366 + smb_ofile_t *of = (smb_ofile_t *)arg;
1367 + smb_tree_t *tree = of->f_tree;
912 1368
913 - ASSERT(of);
914 - ASSERT(of->f_magic == SMB_OFILE_MAGIC);
1369 + SMB_OFILE_VALID(of);
1370 + ASSERT(of->f_refcnt == 0);
1371 + ASSERT(of->f_ftype == SMB_FTYPE_DISK);
1372 + ASSERT(of->f_state == SMB_OFILE_STATE_SAVING);
915 1373
1374 + atomic_dec_32(&of->f_session->s_file_cnt);
1375 + atomic_dec_32(&of->f_tree->t_open_files);
1376 + smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
1377 + smb_llist_remove(&tree->t_ofile_list, of);
1378 + smb_llist_exit(&tree->t_ofile_list);
1379 +
1380 + /*
1381 + * This ofile is no longer on t_ofile_list, however...
1382 + *
1383 + * This is called via smb_llist_post, which means it may run
1384 + * BEFORE smb_ofile_release drops f_mutex (if another thread
1385 + * flushes the delete queue before we do). Synchronize.
1386 + */
916 1387 mutex_enter(&of->f_mutex);
917 - switch (of->f_state) {
918 - case SMB_OFILE_STATE_OPEN:
919 - /* The file is still open. */
920 - of->f_refcnt++;
921 - ASSERT(of->f_refcnt);
922 - tree = of->f_tree;
923 - mutex_exit(&of->f_mutex);
924 - smb_llist_exit(&of->f_tree->t_ofile_list);
925 - smb_ofile_close(of, 0);
926 - smb_ofile_release(of);
927 - smb_llist_enter(&tree->t_ofile_list, RW_READER);
928 - next_of = smb_llist_head(&tree->t_ofile_list);
929 - break;
930 - case SMB_OFILE_STATE_CLOSING:
931 - case SMB_OFILE_STATE_CLOSED:
932 - /*
933 - * The ofile exists but is closed or
934 - * in the process being closed.
935 - */
936 - mutex_exit(&of->f_mutex);
937 - next_of = smb_llist_next(&of->f_tree->t_ofile_list, of);
938 - break;
939 - default:
940 - ASSERT(0);
941 - mutex_exit(&of->f_mutex);
942 - next_of = smb_llist_next(&of->f_tree->t_ofile_list, of);
943 - break;
944 - }
945 - return (next_of);
1388 + DTRACE_PROBE1(ofile__exit, smb_ofile_t, of);
1389 + mutex_exit(&of->f_mutex);
1390 +
1391 + /*
1392 + * Keep f_notify state, lease, and
1393 + * keep on node ofile list.
1394 + * Keep of->f_cr until reclaim.
1395 + */
1396 +
1397 + ASSERT(of->f_fid != 0);
1398 + smb_idpool_free(&tree->t_fid_pool, of->f_fid);
1399 + of->f_fid = 0;
1400 + smb_tree_release(of->f_tree);
1401 + of->f_tree = NULL;
1402 + smb_user_release(of->f_user);
1403 + of->f_user = NULL;
1404 + of->f_session = NULL;
1405 +
1406 + /*
1407 + * Make it "orphaned" so it can now be reclaimed.
1408 + * Note that smb_ofile_hold_olbrk() may have blocked
1409 + * for state SMB_OFILE_STATE_SAVING, so wake it.
1410 + */
1411 + mutex_enter(&of->f_mutex);
1412 + of->dh_expire_time = gethrtime() + of->dh_timeout_offset;
1413 + of->f_state = SMB_OFILE_STATE_ORPHANED;
1414 + cv_broadcast(&of->f_cv);
1415 + mutex_exit(&of->f_mutex);
946 1416 }
947 1417
948 1418 /*
949 1419 * Delete an ofile.
950 1420 *
951 - * Remove the ofile from the tree list before freeing resources
952 - * associated with the ofile.
1421 + * Approximately the inverse of smb_ofile_alloc()
1422 + * Called via smb_llist_post (after smb_llist_exit)
1423 + * when the last ref. on this ofile has gone.
1424 + *
1425 + * Normally,this removes the ofile from the tree list and
1426 + * then frees resources held on the ofile. However, when
1427 + * we're expiring an orphaned durable handle, the linkage
1428 + * into the tree lists etc. have already been destroyed.
1429 + * This case is distinguished by of->f_tree == NULL.
953 1430 */
954 -void
1431 +static void
955 1432 smb_ofile_delete(void *arg)
956 1433 {
957 - smb_tree_t *tree;
958 1434 smb_ofile_t *of = (smb_ofile_t *)arg;
1435 + smb_tree_t *tree = of->f_tree;
959 1436
960 1437 SMB_OFILE_VALID(of);
961 1438 ASSERT(of->f_refcnt == 0);
962 1439 ASSERT(of->f_state == SMB_OFILE_STATE_CLOSED);
963 - ASSERT(!SMB_OFILE_OPLOCK_GRANTED(of));
964 1440
965 - tree = of->f_tree;
966 - smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
967 - smb_llist_remove(&tree->t_ofile_list, of);
968 - smb_idpool_free(&tree->t_fid_pool, of->f_fid);
969 - atomic_dec_32(&tree->t_session->s_file_cnt);
970 - smb_llist_exit(&tree->t_ofile_list);
1441 + if (tree != NULL) {
1442 + ASSERT(of->f_user != NULL);
1443 + ASSERT(of->f_session != NULL);
1444 + atomic_dec_32(&of->f_session->s_file_cnt);
1445 + atomic_dec_32(&of->f_tree->t_open_files);
1446 + smb_llist_enter(&tree->t_ofile_list, RW_WRITER);
1447 + smb_llist_remove(&tree->t_ofile_list, of);
1448 + smb_llist_exit(&tree->t_ofile_list);
1449 + }
971 1450
1451 + /*
1452 + * Remove this ofile from the node's n_ofile_list so it
1453 + * can't be found by list walkers like notify or oplock.
1454 + * Keep the node ref. until later in this function so
1455 + * of->f_node remains valid while we destroy the ofile.
1456 + */
1457 + if (of->f_ftype == SMB_FTYPE_DISK ||
1458 + of->f_ftype == SMB_FTYPE_PRINTER) {
1459 + ASSERT(of->f_node != NULL);
1460 + /*
1461 + * Note smb_ofile_close did smb_node_dec_open_ofiles()
1462 + */
1463 + smb_node_rem_ofile(of->f_node, of);
1464 + }
1465 +
1466 + /*
1467 + * This ofile is no longer on any lists, however...
1468 + *
1469 + * This is called via smb_llist_post, which means it may run
1470 + * BEFORE smb_ofile_release drops f_mutex (if another thread
1471 + * flushes the delete queue before we do). Synchronize.
1472 + */
972 1473 mutex_enter(&of->f_mutex);
1474 + of->f_state = SMB_OFILE_STATE_ALLOC;
1475 + DTRACE_PROBE1(ofile__exit, smb_ofile_t, of);
973 1476 mutex_exit(&of->f_mutex);
974 1477
975 1478 switch (of->f_ftype) {
976 1479 case SMB_FTYPE_BYTE_PIPE:
977 1480 case SMB_FTYPE_MESG_PIPE:
978 1481 smb_opipe_dealloc(of->f_pipe);
979 1482 of->f_pipe = NULL;
980 1483 break;
981 1484 case SMB_FTYPE_DISK:
1485 + ASSERT(of->f_notify.nc_subscribed == B_FALSE);
1486 + MBC_FLUSH(&of->f_notify.nc_buffer);
982 1487 if (of->f_odir != NULL)
983 1488 smb_odir_release(of->f_odir);
984 - smb_node_rem_ofile(of->f_node, of);
1489 + if (of->f_lease != NULL) {
1490 + smb2_lease_rele(of->f_lease);
1491 + of->f_lease = NULL;
1492 + }
1493 + /* FALLTHROUGH */
1494 + case SMB_FTYPE_PRINTER:
1495 + /*
1496 + * Did smb_node_rem_ofile above.
1497 + */
1498 + ASSERT(of->f_node != NULL);
985 1499 smb_node_release(of->f_node);
986 1500 break;
987 1501 default:
988 1502 ASSERT(!"f_ftype");
989 1503 break;
990 1504 }
991 1505
1506 + smb_ofile_free(of);
1507 +}
1508 +
1509 +void
1510 +smb_ofile_free(smb_ofile_t *of)
1511 +{
1512 + smb_tree_t *tree = of->f_tree;
1513 +
1514 + ASSERT(of->f_state == SMB_OFILE_STATE_ALLOC);
1515 +
1516 + /* Make sure it's not in the persistid hash. */
1517 + ASSERT(of->f_persistid == 0);
1518 +
1519 + if (tree != NULL) {
1520 + if (of->f_fid != 0)
1521 + smb_idpool_free(&tree->t_fid_pool, of->f_fid);
1522 + smb_tree_release(of->f_tree);
1523 + smb_user_release(of->f_user);
1524 + }
1525 +
1526 + if (of->f_cr != NULL)
1527 + crfree(of->f_cr);
1528 +
992 1529 of->f_magic = (uint32_t)~SMB_OFILE_MAGIC;
1530 + list_destroy(&of->f_notify.nc_waiters);
1531 + mutex_destroy(&of->dh_nvlock);
993 1532 mutex_destroy(&of->f_mutex);
994 - crfree(of->f_cr);
995 - smb_user_release(of->f_user);
996 1533 kmem_cache_free(smb_cache_ofile, of);
997 1534 }
998 1535
999 1536 /*
1000 1537 * smb_ofile_access
1001 1538 *
1002 1539 * This function will check to see if the access requested is granted.
1003 1540 * Returns NT status codes.
1004 1541 */
1005 1542 uint32_t
1006 1543 smb_ofile_access(smb_ofile_t *of, cred_t *cr, uint32_t access)
1007 1544 {
1008 1545
1009 1546 if ((of == NULL) || (cr == zone_kcred()))
1010 1547 return (NT_STATUS_SUCCESS);
1011 1548
1012 1549 /*
1013 1550 * If the request is for something
1014 1551 * I don't grant it is an error
1015 1552 */
1016 1553 if (~(of->f_granted_access) & access) {
1017 1554 if (!(of->f_granted_access & ACCESS_SYSTEM_SECURITY) &&
1018 1555 (access & ACCESS_SYSTEM_SECURITY)) {
1019 1556 return (NT_STATUS_PRIVILEGE_NOT_HELD);
1020 1557 }
1021 1558 return (NT_STATUS_ACCESS_DENIED);
1022 1559 }
1023 1560
1024 1561 return (NT_STATUS_SUCCESS);
1025 1562 }
1026 1563
1027 1564 /*
1028 1565 * smb_ofile_share_check
1029 1566 *
1030 1567 * Check if ofile was opened with share access NONE (0).
1031 1568 * Returns: B_TRUE - share access non-zero
1032 1569 * B_FALSE - share access NONE
1033 1570 */
1034 1571 boolean_t
1035 1572 smb_ofile_share_check(smb_ofile_t *of)
1036 1573 {
1037 1574 return (!SMB_DENY_ALL(of->f_share_access));
1038 1575 }
1039 1576
1040 1577 /*
|
↓ open down ↓ |
35 lines elided |
↑ open up ↑ |
1041 1578 * check file sharing rules for current open request
1042 1579 * against existing open instances of the same file
1043 1580 *
1044 1581 * Returns NT_STATUS_SHARING_VIOLATION if there is any
1045 1582 * sharing conflict, otherwise returns NT_STATUS_SUCCESS.
1046 1583 */
1047 1584 uint32_t
1048 1585 smb_ofile_open_check(smb_ofile_t *of, uint32_t desired_access,
1049 1586 uint32_t share_access)
1050 1587 {
1588 + uint32_t ret;
1589 +
1051 1590 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
1052 1591
1053 1592 mutex_enter(&of->f_mutex);
1054 1593
1055 - if (of->f_state != SMB_OFILE_STATE_OPEN) {
1056 - mutex_exit(&of->f_mutex);
1057 - return (NT_STATUS_INVALID_HANDLE);
1594 + if (!smb_ofile_is_open_locked(of)) {
1595 + ret = NT_STATUS_INVALID_HANDLE;
1596 + goto out;
1058 1597 }
1059 1598
1060 1599 /* if it's just meta data */
1061 1600 if ((of->f_granted_access & FILE_DATA_ALL) == 0) {
1062 - mutex_exit(&of->f_mutex);
1063 - return (NT_STATUS_SUCCESS);
1601 + ret = NT_STATUS_SUCCESS;
1602 + goto out;
1064 1603 }
1065 1604
1066 1605 /*
1067 1606 * Check requested share access against the
1068 1607 * open granted (desired) access
1069 1608 */
1070 1609 if (SMB_DENY_DELETE(share_access) && (of->f_granted_access & DELETE)) {
1071 - mutex_exit(&of->f_mutex);
1072 - return (NT_STATUS_SHARING_VIOLATION);
1610 + ret = NT_STATUS_SHARING_VIOLATION;
1611 + goto out;
1073 1612 }
1074 1613
1075 1614 if (SMB_DENY_READ(share_access) &&
1076 1615 (of->f_granted_access & (FILE_READ_DATA | FILE_EXECUTE))) {
1077 - mutex_exit(&of->f_mutex);
1078 - return (NT_STATUS_SHARING_VIOLATION);
1616 + ret = NT_STATUS_SHARING_VIOLATION;
1617 + goto out;
1079 1618 }
1080 1619
1081 1620 if (SMB_DENY_WRITE(share_access) &&
1082 1621 (of->f_granted_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
1083 - mutex_exit(&of->f_mutex);
1084 - return (NT_STATUS_SHARING_VIOLATION);
1622 + ret = NT_STATUS_SHARING_VIOLATION;
1623 + goto out;
1085 1624 }
1086 1625
1087 1626 /* check requested desired access against the open share access */
1088 1627 if (SMB_DENY_DELETE(of->f_share_access) && (desired_access & DELETE)) {
1089 - mutex_exit(&of->f_mutex);
1090 - return (NT_STATUS_SHARING_VIOLATION);
1628 + ret = NT_STATUS_SHARING_VIOLATION;
1629 + goto out;
1091 1630 }
1092 1631
1093 1632 if (SMB_DENY_READ(of->f_share_access) &&
1094 1633 (desired_access & (FILE_READ_DATA | FILE_EXECUTE))) {
1095 - mutex_exit(&of->f_mutex);
1096 - return (NT_STATUS_SHARING_VIOLATION);
1634 + ret = NT_STATUS_SHARING_VIOLATION;
1635 + goto out;
1097 1636 }
1098 1637
1099 1638 if (SMB_DENY_WRITE(of->f_share_access) &&
1100 1639 (desired_access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
1101 - mutex_exit(&of->f_mutex);
1102 - return (NT_STATUS_SHARING_VIOLATION);
1640 + ret = NT_STATUS_SHARING_VIOLATION;
1641 + goto out;
1103 1642 }
1104 1643
1644 + ret = NT_STATUS_SUCCESS;
1645 +out:
1105 1646 mutex_exit(&of->f_mutex);
1106 - return (NT_STATUS_SUCCESS);
1647 + return (ret);
1107 1648 }
1108 1649
1109 1650 /*
1110 1651 * smb_ofile_rename_check
1111 1652 *
1112 - * An open file can be renamed if
1113 - *
1114 - * 1. isn't opened for data writing or deleting
1115 - *
1116 - * 2. Opened with "Deny Delete" share mode
1117 - * But not opened for data reading or executing
1118 - * (opened for accessing meta data)
1653 + * This does the work described in MS-FSA 2.1.5.1.2.2 (Algorithm
1654 + * to Check Sharing Access to an Existing Stream or Directory),
1655 + * where the "open in-progress" has DesiredAccess = DELETE and
1656 + * SharingMode = SHARE_READ | SHARE_WRITE | SHARE_DELETE.
1119 1657 */
1120 1658
1121 1659 uint32_t
1122 1660 smb_ofile_rename_check(smb_ofile_t *of)
1123 1661 {
1662 + uint32_t ret;
1663 +
1124 1664 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
1125 1665
1126 1666 mutex_enter(&of->f_mutex);
1127 1667
1128 - if (of->f_state != SMB_OFILE_STATE_OPEN) {
1129 - mutex_exit(&of->f_mutex);
1130 - return (NT_STATUS_INVALID_HANDLE);
1668 + if (!smb_ofile_is_open_locked(of)) {
1669 + ret = NT_STATUS_INVALID_HANDLE;
1670 + goto out;
1131 1671 }
1132 1672
1133 - if (of->f_granted_access &
1134 - (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) {
1135 - mutex_exit(&of->f_mutex);
1136 - return (NT_STATUS_SHARING_VIOLATION);
1673 + if ((of->f_granted_access & FILE_DATA_ALL) == 0) {
1674 + ret = NT_STATUS_SUCCESS;
1675 + goto out;
1137 1676 }
1138 1677
1139 1678 if ((of->f_share_access & FILE_SHARE_DELETE) == 0) {
1140 - if (of->f_granted_access &
1141 - (FILE_READ_DATA | FILE_EXECUTE)) {
1142 - mutex_exit(&of->f_mutex);
1143 - return (NT_STATUS_SHARING_VIOLATION);
1144 - }
1679 + ret = NT_STATUS_SHARING_VIOLATION;
1680 + goto out;
1145 1681 }
1146 1682
1683 + ret = NT_STATUS_SUCCESS;
1684 +out:
1147 1685 mutex_exit(&of->f_mutex);
1148 - return (NT_STATUS_SUCCESS);
1686 + return (ret);
1149 1687 }
1150 1688
1151 1689 /*
1152 1690 * smb_ofile_delete_check
1153 1691 *
1154 1692 * An open file can be deleted only if opened for
1155 1693 * accessing meta data. Share modes aren't important
1156 1694 * in this case.
1157 1695 *
1158 1696 * NOTE: there is another mechanism for deleting an
1159 1697 * open file that NT clients usually use.
1160 1698 * That's setting "Delete on close" flag for an open
1161 1699 * file. In this way the file will be deleted after
|
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
1162 1700 * last close. This flag can be set by SmbTrans2SetFileInfo
1163 1701 * with FILE_DISPOSITION_INFO information level.
1164 1702 * For setting this flag, the file should be opened by
1165 1703 * DELETE access in the FID that is passed in the Trans2
1166 1704 * request.
1167 1705 */
1168 1706
1169 1707 uint32_t
1170 1708 smb_ofile_delete_check(smb_ofile_t *of)
1171 1709 {
1710 + uint32_t ret;
1711 +
1172 1712 ASSERT(of->f_magic == SMB_OFILE_MAGIC);
1173 1713
1174 1714 mutex_enter(&of->f_mutex);
1175 1715
1176 - if (of->f_state != SMB_OFILE_STATE_OPEN) {
1177 - mutex_exit(&of->f_mutex);
1178 - return (NT_STATUS_INVALID_HANDLE);
1716 + if (!smb_ofile_is_open_locked(of)) {
1717 + ret = NT_STATUS_INVALID_HANDLE;
1718 + goto out;
1179 1719 }
1180 1720
1181 1721 if (of->f_granted_access &
1182 1722 (FILE_READ_DATA | FILE_WRITE_DATA |
1183 1723 FILE_APPEND_DATA | FILE_EXECUTE | DELETE)) {
1184 - mutex_exit(&of->f_mutex);
1185 - return (NT_STATUS_SHARING_VIOLATION);
1724 + ret = NT_STATUS_SHARING_VIOLATION;
1725 + goto out;
1186 1726 }
1187 1727
1728 + ret = NT_STATUS_SUCCESS;
1729 +out:
1188 1730 mutex_exit(&of->f_mutex);
1189 - return (NT_STATUS_SUCCESS);
1731 + return (ret);
1190 1732 }
1191 1733
1192 1734 cred_t *
1193 1735 smb_ofile_getcred(smb_ofile_t *of)
1194 1736 {
1195 1737 return (of->f_cr);
1196 1738 }
1197 1739
1198 1740 /*
1199 1741 * smb_ofile_set_delete_on_close
1200 1742 *
1201 1743 * Set the DeleteOnClose flag on the smb file. When the file is closed,
|
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
1202 1744 * the flag will be transferred to the smb node, which will commit the
1203 1745 * delete operation and inhibit subsequent open requests.
1204 1746 *
1205 1747 * When DeleteOnClose is set on an smb_node, the common open code will
1206 1748 * reject subsequent open requests for the file. Observation of Windows
1207 1749 * 2000 indicates that subsequent opens should be allowed (assuming
1208 1750 * there would be no sharing violation) until the file is closed using
1209 1751 * the fid on which the DeleteOnClose was requested.
1210 1752 */
1211 1753 void
1212 -smb_ofile_set_delete_on_close(smb_ofile_t *of)
1754 +smb_ofile_set_delete_on_close(smb_request_t *sr, smb_ofile_t *of)
1213 1755 {
1756 + uint32_t status;
1757 +
1758 + /*
1759 + * Break any oplock handle caching.
1760 + */
1761 + status = smb_oplock_break_SETINFO(of->f_node, of,
1762 + FileDispositionInformation);
1763 + if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
1764 + if (sr->session->dialect >= SMB_VERS_2_BASE)
1765 + (void) smb2sr_go_async(sr);
1766 + (void) smb_oplock_wait_break(of->f_node, 0);
1767 + }
1768 +
1214 1769 mutex_enter(&of->f_mutex);
1215 1770 of->f_flags |= SMB_OFLAGS_SET_DELETE_ON_CLOSE;
1216 1771 mutex_exit(&of->f_mutex);
1217 1772 }
1218 1773
1219 1774 /*
1220 1775 * Encode open file information into a buffer; needed in user space to
1221 1776 * support RPC requests.
1222 1777 */
1223 1778 static int
1224 1779 smb_ofile_netinfo_encode(smb_ofile_t *of, uint8_t *buf, size_t buflen,
1225 1780 uint32_t *nbytes)
1226 1781 {
1227 1782 smb_netfileinfo_t fi;
1228 1783 int rc;
1229 1784
1230 1785 rc = smb_ofile_netinfo_init(of, &fi);
1231 1786 if (rc == 0) {
1232 1787 rc = smb_netfileinfo_encode(&fi, buf, buflen, nbytes);
1233 1788 smb_ofile_netinfo_fini(&fi);
1234 1789 }
1235 1790
1236 1791 return (rc);
1237 1792 }
1238 1793
1239 1794 static int
1240 1795 smb_ofile_netinfo_init(smb_ofile_t *of, smb_netfileinfo_t *fi)
1241 1796 {
1242 1797 smb_user_t *user;
1243 1798 smb_tree_t *tree;
1244 1799 smb_node_t *node;
1245 1800 char *path;
1246 1801 char *buf;
1247 1802 int rc;
1248 1803
1249 1804 ASSERT(of);
1250 1805 user = of->f_user;
1251 1806 tree = of->f_tree;
1252 1807 ASSERT(user);
1253 1808 ASSERT(tree);
1254 1809
1255 1810 buf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1256 1811
1257 1812 switch (of->f_ftype) {
1258 1813 case SMB_FTYPE_DISK:
1259 1814 node = of->f_node;
1260 1815 ASSERT(node);
1261 1816
1262 1817 fi->fi_permissions = of->f_granted_access;
1263 1818 fi->fi_numlocks = smb_lock_get_lock_count(node, of);
1264 1819
1265 1820 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1266 1821
1267 1822 if (node != tree->t_snode) {
1268 1823 rc = smb_node_getshrpath(node, tree, path, MAXPATHLEN);
1269 1824 if (rc != 0)
1270 1825 (void) strlcpy(path, node->od_name, MAXPATHLEN);
1271 1826 }
1272 1827
1273 1828 (void) snprintf(buf, MAXPATHLEN, "%s:%s", tree->t_sharename,
1274 1829 path);
1275 1830 kmem_free(path, MAXPATHLEN);
1276 1831 break;
1277 1832
1278 1833 case SMB_FTYPE_MESG_PIPE:
1279 1834 ASSERT(of->f_pipe);
1280 1835
1281 1836 fi->fi_permissions = FILE_READ_DATA | FILE_WRITE_DATA |
1282 1837 FILE_EXECUTE;
1283 1838 fi->fi_numlocks = 0;
1284 1839 (void) snprintf(buf, MAXPATHLEN, "\\PIPE\\%s",
1285 1840 of->f_pipe->p_name);
1286 1841 break;
1287 1842
1288 1843 default:
1289 1844 kmem_free(buf, MAXPATHLEN);
1290 1845 return (-1);
1291 1846 }
1292 1847
1293 1848 fi->fi_fid = of->f_fid;
1294 1849 fi->fi_uniqid = of->f_uniqid;
1295 1850 fi->fi_pathlen = strlen(buf) + 1;
1296 1851 fi->fi_path = smb_mem_strdup(buf);
1297 1852 kmem_free(buf, MAXPATHLEN);
1298 1853
1299 1854 fi->fi_namelen = user->u_domain_len + user->u_name_len + 2;
1300 1855 fi->fi_username = kmem_alloc(fi->fi_namelen, KM_SLEEP);
1301 1856 (void) snprintf(fi->fi_username, fi->fi_namelen, "%s\\%s",
1302 1857 user->u_domain, user->u_name);
1303 1858 return (0);
1304 1859 }
1305 1860
1306 1861 static void
1307 1862 smb_ofile_netinfo_fini(smb_netfileinfo_t *fi)
1308 1863 {
1309 1864 if (fi == NULL)
1310 1865 return;
1311 1866
1312 1867 if (fi->fi_path)
1313 1868 smb_mem_free(fi->fi_path);
1314 1869 if (fi->fi_username)
1315 1870 kmem_free(fi->fi_username, fi->fi_namelen);
1316 1871
1317 1872 bzero(fi, sizeof (smb_netfileinfo_t));
1318 1873 }
1319 1874
1320 1875 /*
1321 1876 * A query of user and group quotas may span multiple requests.
1322 1877 * f_quota_resume is used to determine where the query should
1323 1878 * be resumed, in a subsequent request. f_quota_resume contains
1324 1879 * the SID of the last quota entry returned to the client.
1325 1880 */
1326 1881 void
1327 1882 smb_ofile_set_quota_resume(smb_ofile_t *ofile, char *resume)
1328 1883 {
1329 1884 ASSERT(ofile);
1330 1885 mutex_enter(&ofile->f_mutex);
1331 1886 if (resume == NULL)
1332 1887 bzero(ofile->f_quota_resume, SMB_SID_STRSZ);
1333 1888 else
1334 1889 (void) strlcpy(ofile->f_quota_resume, resume, SMB_SID_STRSZ);
1335 1890 mutex_exit(&ofile->f_mutex);
1336 1891 }
1337 1892
1338 1893 void
1339 1894 smb_ofile_get_quota_resume(smb_ofile_t *ofile, char *buf, int bufsize)
1340 1895 {
1341 1896 ASSERT(ofile);
1342 1897 mutex_enter(&ofile->f_mutex);
1343 1898 (void) strlcpy(buf, ofile->f_quota_resume, bufsize);
1344 1899 mutex_exit(&ofile->f_mutex);
1345 1900 }
|
↓ open down ↓ |
122 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX