1 | // SPDX-License-Identifier: LGPL-2.1 |
---|---|
2 | /* |
3 | * |
4 | * Copyright (c) International Business Machines Corp., 2003, 2007 |
5 | * Author(s): Steve French (sfrench@us.ibm.com) |
6 | * |
7 | */ |
8 | |
9 | #include <linux/fs.h> |
10 | #include <linux/posix_acl_xattr.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/xattr.h> |
13 | #include "cifsfs.h" |
14 | #include "cifspdu.h" |
15 | #include "cifsglob.h" |
16 | #include "cifsproto.h" |
17 | #include "cifs_debug.h" |
18 | #include "cifs_fs_sb.h" |
19 | #include "cifs_unicode.h" |
20 | #include "cifs_ioctl.h" |
21 | |
22 | #define MAX_EA_VALUE_SIZE CIFSMaxBufSize |
23 | #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* DACL only */ |
24 | #define CIFS_XATTR_CIFS_NTSD "system.cifs_ntsd" /* owner plus DACL */ |
25 | #define CIFS_XATTR_CIFS_NTSD_FULL "system.cifs_ntsd_full" /* owner/DACL/SACL */ |
26 | #define CIFS_XATTR_ATTRIB "cifs.dosattrib" /* full name: user.cifs.dosattrib */ |
27 | #define CIFS_XATTR_CREATETIME "cifs.creationtime" /* user.cifs.creationtime */ |
28 | /* |
29 | * Although these three are just aliases for the above, need to move away from |
30 | * confusing users and using the 20+ year old term 'cifs' when it is no longer |
31 | * secure, replaced by SMB2 (then even more highly secure SMB3) many years ago |
32 | */ |
33 | #define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */ |
34 | #define SMB3_XATTR_CIFS_NTSD_SACL "system.smb3_ntsd_sacl" /* SACL only */ |
35 | #define SMB3_XATTR_CIFS_NTSD_OWNER "system.smb3_ntsd_owner" /* owner only */ |
36 | #define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */ |
37 | #define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */ |
38 | #define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */ |
39 | #define SMB3_XATTR_CREATETIME "smb3.creationtime" /* user.smb3.creationtime */ |
40 | /* BB need to add server (Samba e.g) support for security and trusted prefix */ |
41 | |
42 | enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT, |
43 | XATTR_CIFS_NTSD_SACL, XATTR_CIFS_NTSD_OWNER, |
44 | XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL }; |
45 | |
46 | static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon, |
47 | struct inode *inode, const char *full_path, |
48 | const void *value, size_t size) |
49 | { |
50 | ssize_t rc = -EOPNOTSUPP; |
51 | __u32 *pattrib = (__u32 *)value; |
52 | __u32 attrib; |
53 | FILE_BASIC_INFO info_buf; |
54 | |
55 | if ((value == NULL) || (size != sizeof(__u32))) |
56 | return -ERANGE; |
57 | |
58 | memset(&info_buf, 0, sizeof(info_buf)); |
59 | attrib = *pattrib; |
60 | info_buf.Attributes = cpu_to_le32(attrib); |
61 | if (pTcon->ses->server->ops->set_file_info) |
62 | rc = pTcon->ses->server->ops->set_file_info(inode, full_path, |
63 | &info_buf, xid); |
64 | if (rc == 0) |
65 | CIFS_I(inode)->cifsAttrs = attrib; |
66 | |
67 | return rc; |
68 | } |
69 | |
70 | static int cifs_creation_time_set(unsigned int xid, struct cifs_tcon *pTcon, |
71 | struct inode *inode, const char *full_path, |
72 | const void *value, size_t size) |
73 | { |
74 | ssize_t rc = -EOPNOTSUPP; |
75 | __u64 *pcreation_time = (__u64 *)value; |
76 | __u64 creation_time; |
77 | FILE_BASIC_INFO info_buf; |
78 | |
79 | if ((value == NULL) || (size != sizeof(__u64))) |
80 | return -ERANGE; |
81 | |
82 | memset(&info_buf, 0, sizeof(info_buf)); |
83 | creation_time = *pcreation_time; |
84 | info_buf.CreationTime = cpu_to_le64(creation_time); |
85 | if (pTcon->ses->server->ops->set_file_info) |
86 | rc = pTcon->ses->server->ops->set_file_info(inode, full_path, |
87 | &info_buf, xid); |
88 | if (rc == 0) |
89 | CIFS_I(inode)->createtime = creation_time; |
90 | |
91 | return rc; |
92 | } |
93 | |
94 | static int cifs_xattr_set(const struct xattr_handler *handler, |
95 | struct mnt_idmap *idmap, |
96 | struct dentry *dentry, struct inode *inode, |
97 | const char *name, const void *value, |
98 | size_t size, int flags) |
99 | { |
100 | int rc = -EOPNOTSUPP; |
101 | unsigned int xid; |
102 | struct super_block *sb = dentry->d_sb; |
103 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
104 | struct tcon_link *tlink; |
105 | struct cifs_tcon *pTcon; |
106 | const char *full_path; |
107 | void *page; |
108 | |
109 | tlink = cifs_sb_tlink(cifs_sb); |
110 | if (IS_ERR(ptr: tlink)) |
111 | return PTR_ERR(ptr: tlink); |
112 | pTcon = tlink_tcon(tlink); |
113 | |
114 | xid = get_xid(); |
115 | page = alloc_dentry_path(); |
116 | |
117 | full_path = build_path_from_dentry(dentry, page); |
118 | if (IS_ERR(ptr: full_path)) { |
119 | rc = PTR_ERR(ptr: full_path); |
120 | goto out; |
121 | } |
122 | /* return dos attributes as pseudo xattr */ |
123 | /* return alt name if available as pseudo attr */ |
124 | |
125 | /* if proc/fs/cifs/streamstoxattr is set then |
126 | search server for EAs or streams to |
127 | returns as xattrs */ |
128 | if (size > MAX_EA_VALUE_SIZE) { |
129 | cifs_dbg(FYI, "size of EA value too large\n"); |
130 | rc = -EOPNOTSUPP; |
131 | goto out; |
132 | } |
133 | |
134 | switch (handler->flags) { |
135 | case XATTR_USER: |
136 | cifs_dbg(FYI, "%s:setting user xattr %s\n", __func__, name); |
137 | if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || |
138 | (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { |
139 | rc = cifs_attrib_set(xid, pTcon, inode, full_path, |
140 | value, size); |
141 | if (rc == 0) /* force revalidate of the inode */ |
142 | CIFS_I(inode)->time = 0; |
143 | break; |
144 | } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || |
145 | (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { |
146 | rc = cifs_creation_time_set(xid, pTcon, inode, |
147 | full_path, value, size); |
148 | if (rc == 0) /* force revalidate of the inode */ |
149 | CIFS_I(inode)->time = 0; |
150 | break; |
151 | } |
152 | |
153 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
154 | goto out; |
155 | |
156 | if (pTcon->ses->server->ops->set_EA) { |
157 | rc = pTcon->ses->server->ops->set_EA(xid, pTcon, |
158 | full_path, name, value, (__u16)size, |
159 | cifs_sb->local_nls, cifs_sb); |
160 | if (rc == 0) |
161 | inode_set_ctime_current(inode); |
162 | } |
163 | break; |
164 | |
165 | case XATTR_CIFS_ACL: |
166 | case XATTR_CIFS_NTSD_SACL: |
167 | case XATTR_CIFS_NTSD_OWNER: |
168 | case XATTR_CIFS_NTSD: |
169 | case XATTR_CIFS_NTSD_FULL: { |
170 | struct smb_ntsd *pacl; |
171 | |
172 | if (!value) |
173 | goto out; |
174 | pacl = kmalloc(size, GFP_KERNEL); |
175 | if (!pacl) { |
176 | rc = -ENOMEM; |
177 | } else { |
178 | memcpy(pacl, value, size); |
179 | if (pTcon->ses->server->ops->set_acl) { |
180 | int aclflags = 0; |
181 | rc = 0; |
182 | |
183 | switch (handler->flags) { |
184 | case XATTR_CIFS_NTSD_FULL: |
185 | aclflags = (CIFS_ACL_OWNER | |
186 | CIFS_ACL_GROUP | |
187 | CIFS_ACL_DACL | |
188 | CIFS_ACL_SACL); |
189 | break; |
190 | case XATTR_CIFS_NTSD: |
191 | aclflags = (CIFS_ACL_OWNER | |
192 | CIFS_ACL_GROUP | |
193 | CIFS_ACL_DACL); |
194 | break; |
195 | case XATTR_CIFS_NTSD_OWNER: |
196 | aclflags = (CIFS_ACL_OWNER | |
197 | CIFS_ACL_GROUP); |
198 | break; |
199 | case XATTR_CIFS_NTSD_SACL: |
200 | aclflags = CIFS_ACL_SACL; |
201 | break; |
202 | case XATTR_CIFS_ACL: |
203 | default: |
204 | aclflags = CIFS_ACL_DACL; |
205 | } |
206 | |
207 | rc = pTcon->ses->server->ops->set_acl(pacl, |
208 | size, inode, full_path, aclflags); |
209 | } else { |
210 | rc = -EOPNOTSUPP; |
211 | } |
212 | if (rc == 0) /* force revalidate of the inode */ |
213 | CIFS_I(inode)->time = 0; |
214 | kfree(objp: pacl); |
215 | } |
216 | break; |
217 | } |
218 | } |
219 | |
220 | out: |
221 | free_dentry_path(page); |
222 | free_xid(xid); |
223 | cifs_put_tlink(tlink); |
224 | return rc; |
225 | } |
226 | |
227 | static int cifs_attrib_get(struct dentry *dentry, |
228 | struct inode *inode, void *value, |
229 | size_t size) |
230 | { |
231 | ssize_t rc; |
232 | __u32 *pattribute; |
233 | |
234 | rc = cifs_revalidate_dentry_attr(dentry); |
235 | |
236 | if (rc) |
237 | return rc; |
238 | |
239 | if ((value == NULL) || (size == 0)) |
240 | return sizeof(__u32); |
241 | else if (size < sizeof(__u32)) |
242 | return -ERANGE; |
243 | |
244 | /* return dos attributes as pseudo xattr */ |
245 | pattribute = (__u32 *)value; |
246 | *pattribute = CIFS_I(inode)->cifsAttrs; |
247 | |
248 | return sizeof(__u32); |
249 | } |
250 | |
251 | static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode, |
252 | void *value, size_t size) |
253 | { |
254 | ssize_t rc; |
255 | __u64 *pcreatetime; |
256 | |
257 | rc = cifs_revalidate_dentry_attr(dentry); |
258 | if (rc) |
259 | return rc; |
260 | |
261 | if ((value == NULL) || (size == 0)) |
262 | return sizeof(__u64); |
263 | else if (size < sizeof(__u64)) |
264 | return -ERANGE; |
265 | |
266 | /* return dos attributes as pseudo xattr */ |
267 | pcreatetime = (__u64 *)value; |
268 | *pcreatetime = CIFS_I(inode)->createtime; |
269 | return sizeof(__u64); |
270 | } |
271 | |
272 | |
273 | static int cifs_xattr_get(const struct xattr_handler *handler, |
274 | struct dentry *dentry, struct inode *inode, |
275 | const char *name, void *value, size_t size) |
276 | { |
277 | ssize_t rc = -EOPNOTSUPP; |
278 | unsigned int xid; |
279 | struct super_block *sb = dentry->d_sb; |
280 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
281 | struct tcon_link *tlink; |
282 | struct cifs_tcon *pTcon; |
283 | const char *full_path; |
284 | void *page; |
285 | |
286 | tlink = cifs_sb_tlink(cifs_sb); |
287 | if (IS_ERR(ptr: tlink)) |
288 | return PTR_ERR(ptr: tlink); |
289 | pTcon = tlink_tcon(tlink); |
290 | |
291 | xid = get_xid(); |
292 | page = alloc_dentry_path(); |
293 | |
294 | full_path = build_path_from_dentry(dentry, page); |
295 | if (IS_ERR(ptr: full_path)) { |
296 | rc = PTR_ERR(ptr: full_path); |
297 | goto out; |
298 | } |
299 | |
300 | /* return alt name if available as pseudo attr */ |
301 | switch (handler->flags) { |
302 | case XATTR_USER: |
303 | cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name); |
304 | if ((strcmp(name, CIFS_XATTR_ATTRIB) == 0) || |
305 | (strcmp(name, SMB3_XATTR_ATTRIB) == 0)) { |
306 | rc = cifs_attrib_get(dentry, inode, value, size); |
307 | break; |
308 | } else if ((strcmp(name, CIFS_XATTR_CREATETIME) == 0) || |
309 | (strcmp(name, SMB3_XATTR_CREATETIME) == 0)) { |
310 | rc = cifs_creation_time_get(dentry, inode, value, size); |
311 | break; |
312 | } |
313 | |
314 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
315 | goto out; |
316 | |
317 | if (pTcon->ses->server->ops->query_all_EAs) |
318 | rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, |
319 | full_path, name, value, size, cifs_sb); |
320 | break; |
321 | |
322 | case XATTR_CIFS_ACL: |
323 | case XATTR_CIFS_NTSD_SACL: |
324 | case XATTR_CIFS_NTSD_OWNER: |
325 | case XATTR_CIFS_NTSD: |
326 | case XATTR_CIFS_NTSD_FULL: { |
327 | /* |
328 | * fetch owner, DACL, and SACL if asked for full descriptor, |
329 | * fetch owner and DACL otherwise |
330 | */ |
331 | u32 acllen, extra_info; |
332 | struct smb_ntsd *pacl; |
333 | |
334 | if (pTcon->ses->server->ops->get_acl == NULL) |
335 | goto out; /* rc already EOPNOTSUPP */ |
336 | |
337 | switch (handler->flags) { |
338 | case XATTR_CIFS_NTSD_FULL: |
339 | extra_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO | SACL_SECINFO; |
340 | break; |
341 | case XATTR_CIFS_NTSD: |
342 | extra_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO; |
343 | break; |
344 | case XATTR_CIFS_NTSD_OWNER: |
345 | extra_info = OWNER_SECINFO | GROUP_SECINFO; |
346 | break; |
347 | case XATTR_CIFS_NTSD_SACL: |
348 | extra_info = SACL_SECINFO; |
349 | break; |
350 | case XATTR_CIFS_ACL: |
351 | default: |
352 | extra_info = DACL_SECINFO; |
353 | break; |
354 | } |
355 | pacl = pTcon->ses->server->ops->get_acl(cifs_sb, |
356 | inode, full_path, &acllen, extra_info); |
357 | if (IS_ERR(ptr: pacl)) { |
358 | rc = PTR_ERR(ptr: pacl); |
359 | cifs_dbg(VFS, "%s: error %zd getting sec desc\n", |
360 | __func__, rc); |
361 | } else { |
362 | if (value) { |
363 | if (acllen > size) |
364 | acllen = -ERANGE; |
365 | else |
366 | memcpy(value, pacl, acllen); |
367 | } |
368 | rc = acllen; |
369 | kfree(objp: pacl); |
370 | } |
371 | break; |
372 | } |
373 | } |
374 | |
375 | /* We could add an additional check for streams ie |
376 | if proc/fs/cifs/streamstoxattr is set then |
377 | search server for EAs or streams to |
378 | returns as xattrs */ |
379 | |
380 | if (rc == -EINVAL) |
381 | rc = -EOPNOTSUPP; |
382 | |
383 | out: |
384 | free_dentry_path(page); |
385 | free_xid(xid); |
386 | cifs_put_tlink(tlink); |
387 | return rc; |
388 | } |
389 | |
390 | ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) |
391 | { |
392 | ssize_t rc = -EOPNOTSUPP; |
393 | unsigned int xid; |
394 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb: direntry->d_sb); |
395 | struct tcon_link *tlink; |
396 | struct cifs_tcon *pTcon; |
397 | const char *full_path; |
398 | void *page; |
399 | |
400 | if (unlikely(cifs_forced_shutdown(cifs_sb))) |
401 | return -EIO; |
402 | |
403 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) |
404 | return -EOPNOTSUPP; |
405 | |
406 | tlink = cifs_sb_tlink(cifs_sb); |
407 | if (IS_ERR(ptr: tlink)) |
408 | return PTR_ERR(ptr: tlink); |
409 | pTcon = tlink_tcon(tlink); |
410 | |
411 | xid = get_xid(); |
412 | page = alloc_dentry_path(); |
413 | |
414 | full_path = build_path_from_dentry(direntry, page); |
415 | if (IS_ERR(ptr: full_path)) { |
416 | rc = PTR_ERR(ptr: full_path); |
417 | goto list_ea_exit; |
418 | } |
419 | /* return dos attributes as pseudo xattr */ |
420 | /* return alt name if available as pseudo attr */ |
421 | |
422 | /* if proc/fs/cifs/streamstoxattr is set then |
423 | search server for EAs or streams to |
424 | returns as xattrs */ |
425 | |
426 | if (pTcon->ses->server->ops->query_all_EAs) |
427 | rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, |
428 | full_path, NULL, data, buf_size, cifs_sb); |
429 | list_ea_exit: |
430 | free_dentry_path(page); |
431 | free_xid(xid); |
432 | cifs_put_tlink(tlink); |
433 | return rc; |
434 | } |
435 | |
436 | static const struct xattr_handler cifs_user_xattr_handler = { |
437 | .prefix = XATTR_USER_PREFIX, |
438 | .flags = XATTR_USER, |
439 | .get = cifs_xattr_get, |
440 | .set = cifs_xattr_set, |
441 | }; |
442 | |
443 | /* os2.* attributes are treated like user.* attributes */ |
444 | static const struct xattr_handler cifs_os2_xattr_handler = { |
445 | .prefix = XATTR_OS2_PREFIX, |
446 | .flags = XATTR_USER, |
447 | .get = cifs_xattr_get, |
448 | .set = cifs_xattr_set, |
449 | }; |
450 | |
451 | static const struct xattr_handler cifs_cifs_acl_xattr_handler = { |
452 | .name = CIFS_XATTR_CIFS_ACL, |
453 | .flags = XATTR_CIFS_ACL, |
454 | .get = cifs_xattr_get, |
455 | .set = cifs_xattr_set, |
456 | }; |
457 | |
458 | /* |
459 | * Although this is just an alias for the above, need to move away from |
460 | * confusing users and using the 20 year old term 'cifs' when it is no |
461 | * longer secure and was replaced by SMB2/SMB3 a long time ago, and |
462 | * SMB3 and later are highly secure. |
463 | */ |
464 | static const struct xattr_handler smb3_acl_xattr_handler = { |
465 | .name = SMB3_XATTR_CIFS_ACL, |
466 | .flags = XATTR_CIFS_ACL, |
467 | .get = cifs_xattr_get, |
468 | .set = cifs_xattr_set, |
469 | }; |
470 | |
471 | static const struct xattr_handler smb3_ntsd_sacl_xattr_handler = { |
472 | .name = SMB3_XATTR_CIFS_NTSD_SACL, |
473 | .flags = XATTR_CIFS_NTSD_SACL, |
474 | .get = cifs_xattr_get, |
475 | .set = cifs_xattr_set, |
476 | }; |
477 | |
478 | static const struct xattr_handler smb3_ntsd_owner_xattr_handler = { |
479 | .name = SMB3_XATTR_CIFS_NTSD_OWNER, |
480 | .flags = XATTR_CIFS_NTSD_OWNER, |
481 | .get = cifs_xattr_get, |
482 | .set = cifs_xattr_set, |
483 | }; |
484 | |
485 | static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = { |
486 | .name = CIFS_XATTR_CIFS_NTSD, |
487 | .flags = XATTR_CIFS_NTSD, |
488 | .get = cifs_xattr_get, |
489 | .set = cifs_xattr_set, |
490 | }; |
491 | |
492 | /* |
493 | * Although this is just an alias for the above, need to move away from |
494 | * confusing users and using the 20 year old term 'cifs' when it is no |
495 | * longer secure and was replaced by SMB2/SMB3 a long time ago, and |
496 | * SMB3 and later are highly secure. |
497 | */ |
498 | static const struct xattr_handler smb3_ntsd_xattr_handler = { |
499 | .name = SMB3_XATTR_CIFS_NTSD, |
500 | .flags = XATTR_CIFS_NTSD, |
501 | .get = cifs_xattr_get, |
502 | .set = cifs_xattr_set, |
503 | }; |
504 | |
505 | static const struct xattr_handler cifs_cifs_ntsd_full_xattr_handler = { |
506 | .name = CIFS_XATTR_CIFS_NTSD_FULL, |
507 | .flags = XATTR_CIFS_NTSD_FULL, |
508 | .get = cifs_xattr_get, |
509 | .set = cifs_xattr_set, |
510 | }; |
511 | |
512 | /* |
513 | * Although this is just an alias for the above, need to move away from |
514 | * confusing users and using the 20 year old term 'cifs' when it is no |
515 | * longer secure and was replaced by SMB2/SMB3 a long time ago, and |
516 | * SMB3 and later are highly secure. |
517 | */ |
518 | static const struct xattr_handler smb3_ntsd_full_xattr_handler = { |
519 | .name = SMB3_XATTR_CIFS_NTSD_FULL, |
520 | .flags = XATTR_CIFS_NTSD_FULL, |
521 | .get = cifs_xattr_get, |
522 | .set = cifs_xattr_set, |
523 | }; |
524 | |
525 | const struct xattr_handler * const cifs_xattr_handlers[] = { |
526 | &cifs_user_xattr_handler, |
527 | &cifs_os2_xattr_handler, |
528 | &cifs_cifs_acl_xattr_handler, |
529 | &smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */ |
530 | &smb3_ntsd_sacl_xattr_handler, |
531 | &smb3_ntsd_owner_xattr_handler, |
532 | &cifs_cifs_ntsd_xattr_handler, |
533 | &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */ |
534 | &cifs_cifs_ntsd_full_xattr_handler, |
535 | &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */ |
536 | NULL |
537 | }; |
538 |
Definitions
- cifs_attrib_set
- cifs_creation_time_set
- cifs_xattr_set
- cifs_attrib_get
- cifs_creation_time_get
- cifs_xattr_get
- cifs_listxattr
- cifs_user_xattr_handler
- cifs_os2_xattr_handler
- cifs_cifs_acl_xattr_handler
- smb3_acl_xattr_handler
- smb3_ntsd_sacl_xattr_handler
- smb3_ntsd_owner_xattr_handler
- cifs_cifs_ntsd_xattr_handler
- smb3_ntsd_xattr_handler
- cifs_cifs_ntsd_full_xattr_handler
- smb3_ntsd_full_xattr_handler
Improve your Profiling and Debugging skills
Find out more