1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or https://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2012, 2018 by Delphix. All rights reserved.
24 * Copyright (c) 2014 Integros [integros.com]
25 */
26
27 /* Portions Copyright 2007 Jeremy Teo */
28 /* Portions Copyright 2011 Martin Matuska <mm@FreeBSD.org> */
29
30 #include <sys/dmu.h>
31 #include <sys/dmu_objset.h>
32 #include <sys/dmu_tx.h>
33 #include <sys/zfs_refcount.h>
34 #include <sys/stat.h>
35 #include <sys/zap.h>
36 #include <sys/zfs_znode.h>
37 #include <sys/sa.h>
38 #include <sys/zfs_sa.h>
39 #include <sys/zfs_stat.h>
40
41 #include "zfs_prop.h"
42 #include "zfs_comutil.h"
43
44 static int
zfs_sa_setup(objset_t * osp,sa_attr_type_t ** sa_table)45 zfs_sa_setup(objset_t *osp, sa_attr_type_t **sa_table)
46 {
47 uint64_t sa_obj = 0;
48 int error;
49
50 error = zap_lookup(osp, MASTER_NODE_OBJ, ZFS_SA_ATTRS, 8, 1, &sa_obj);
51 if (error != 0 && error != ENOENT)
52 return (error);
53
54 error = sa_setup(osp, sa_obj, zfs_attr_table, ZPL_END, sa_table);
55 return (error);
56 }
57
58 static int
zfs_grab_sa_handle(objset_t * osp,uint64_t obj,sa_handle_t ** hdlp,dmu_buf_t ** db,const void * tag)59 zfs_grab_sa_handle(objset_t *osp, uint64_t obj, sa_handle_t **hdlp,
60 dmu_buf_t **db, const void *tag)
61 {
62 dmu_object_info_t doi;
63 int error;
64
65 if ((error = sa_buf_hold(osp, obj, tag, db)) != 0)
66 return (error);
67
68 dmu_object_info_from_db(*db, &doi);
69 if ((doi.doi_bonus_type != DMU_OT_SA &&
70 doi.doi_bonus_type != DMU_OT_ZNODE) ||
71 (doi.doi_bonus_type == DMU_OT_ZNODE &&
72 doi.doi_bonus_size < sizeof (znode_phys_t))) {
73 sa_buf_rele(*db, tag);
74 return (SET_ERROR(ENOTSUP));
75 }
76
77 error = sa_handle_get(osp, obj, NULL, SA_HDL_PRIVATE, hdlp);
78 if (error != 0) {
79 sa_buf_rele(*db, tag);
80 return (error);
81 }
82
83 return (0);
84 }
85
86 static void
zfs_release_sa_handle(sa_handle_t * hdl,dmu_buf_t * db,const void * tag)87 zfs_release_sa_handle(sa_handle_t *hdl, dmu_buf_t *db, const void *tag)
88 {
89 sa_handle_destroy(hdl);
90 sa_buf_rele(db, tag);
91 }
92
93 /*
94 * Given an object number, return its parent object number and whether
95 * or not the object is an extended attribute directory.
96 */
97 int
zfs_obj_to_pobj(objset_t * osp,sa_handle_t * hdl,sa_attr_type_t * sa_table,uint64_t * pobjp,int * is_xattrdir)98 zfs_obj_to_pobj(objset_t *osp, sa_handle_t *hdl, sa_attr_type_t *sa_table,
99 uint64_t *pobjp, int *is_xattrdir)
100 {
101 uint64_t parent;
102 uint64_t pflags;
103 uint64_t mode;
104 uint64_t parent_mode;
105 sa_bulk_attr_t bulk[3];
106 sa_handle_t *sa_hdl;
107 dmu_buf_t *sa_db;
108 int count = 0;
109 int error;
110
111 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_PARENT], NULL,
112 &parent, sizeof (parent));
113 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_FLAGS], NULL,
114 &pflags, sizeof (pflags));
115 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL,
116 &mode, sizeof (mode));
117
118 if ((error = sa_bulk_lookup(hdl, bulk, count)) != 0)
119 return (error);
120
121 /*
122 * When a link is removed its parent pointer is not changed and will
123 * be invalid. There are two cases where a link is removed but the
124 * file stays around, when it goes to the delete queue and when there
125 * are additional links.
126 */
127 error = zfs_grab_sa_handle(osp, parent, &sa_hdl, &sa_db, FTAG);
128 if (error != 0)
129 return (error);
130
131 error = sa_lookup(sa_hdl, ZPL_MODE, &parent_mode, sizeof (parent_mode));
132 zfs_release_sa_handle(sa_hdl, sa_db, FTAG);
133 if (error != 0)
134 return (error);
135
136 *is_xattrdir = ((pflags & ZFS_XATTR) != 0) && S_ISDIR(mode);
137
138 /*
139 * Extended attributes can be applied to files, directories, etc.
140 * Otherwise the parent must be a directory.
141 */
142 if (!*is_xattrdir && !S_ISDIR(parent_mode))
143 return (SET_ERROR(EINVAL));
144
145 *pobjp = parent;
146
147 return (0);
148 }
149
150 /*
151 * Given an object number, return some zpl level statistics
152 */
153 static int
zfs_obj_to_stats_impl(sa_handle_t * hdl,sa_attr_type_t * sa_table,zfs_stat_t * sb)154 zfs_obj_to_stats_impl(sa_handle_t *hdl, sa_attr_type_t *sa_table,
155 zfs_stat_t *sb)
156 {
157 sa_bulk_attr_t bulk[4];
158 int count = 0;
159
160 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL,
161 &sb->zs_mode, sizeof (sb->zs_mode));
162 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL,
163 &sb->zs_gen, sizeof (sb->zs_gen));
164 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_LINKS], NULL,
165 &sb->zs_links, sizeof (sb->zs_links));
166 SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_CTIME], NULL,
167 &sb->zs_ctime, sizeof (sb->zs_ctime));
168
169 return (sa_bulk_lookup(hdl, bulk, count));
170 }
171
172 static int
zfs_obj_to_path_impl(objset_t * osp,uint64_t obj,sa_handle_t * hdl,sa_attr_type_t * sa_table,char * buf,int len)173 zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl,
174 sa_attr_type_t *sa_table, char *buf, int len)
175 {
176 sa_handle_t *sa_hdl;
177 sa_handle_t *prevhdl = NULL;
178 dmu_buf_t *prevdb = NULL;
179 dmu_buf_t *sa_db = NULL;
180 char *path = buf + len - 1;
181 char *comp_buf;
182 int error;
183
184 *path = '\0';
185 sa_hdl = hdl;
186
187 uint64_t deleteq_obj;
188 VERIFY0(zap_lookup(osp, MASTER_NODE_OBJ,
189 ZFS_UNLINKED_SET, sizeof (uint64_t), 1, &deleteq_obj));
190 error = zap_lookup_int(osp, deleteq_obj, obj);
191 if (error == 0) {
192 return (ESTALE);
193 } else if (error != ENOENT) {
194 return (error);
195 }
196
197 comp_buf = kmem_alloc(ZAP_MAXNAMELEN_NEW + 2, KM_SLEEP);
198 for (;;) {
199 uint64_t pobj = 0;
200 char *component = comp_buf;
201 size_t complen;
202 int is_xattrdir = 0;
203
204 if (prevdb) {
205 ASSERT3P(prevhdl, !=, NULL);
206 zfs_release_sa_handle(prevhdl, prevdb, FTAG);
207 }
208
209 if ((error = zfs_obj_to_pobj(osp, sa_hdl, sa_table, &pobj,
210 &is_xattrdir)) != 0)
211 break;
212
213 if (pobj == obj) {
214 if (path[0] != '/')
215 *--path = '/';
216 break;
217 }
218
219 component[0] = '/';
220 if (is_xattrdir) {
221 strcpy(component + 1, "<xattrdir>");
222 } else {
223 error = zap_value_search(osp, pobj, obj,
224 ZFS_DIRENT_OBJ(-1ULL), component + 1,
225 ZAP_MAXNAMELEN_NEW);
226 if (error != 0)
227 break;
228 }
229
230 complen = strlen(component);
231 path -= complen;
232 ASSERT3P(path, >=, buf);
233 memcpy(path, component, complen);
234 obj = pobj;
235
236 if (sa_hdl != hdl) {
237 prevhdl = sa_hdl;
238 prevdb = sa_db;
239 }
240 error = zfs_grab_sa_handle(osp, obj, &sa_hdl, &sa_db, FTAG);
241 if (error != 0) {
242 sa_hdl = prevhdl;
243 sa_db = prevdb;
244 break;
245 }
246 }
247
248 if (sa_hdl != NULL && sa_hdl != hdl) {
249 ASSERT3P(sa_db, !=, NULL);
250 zfs_release_sa_handle(sa_hdl, sa_db, FTAG);
251 }
252
253 if (error == 0)
254 (void) memmove(buf, path, buf + len - path);
255
256 kmem_free(comp_buf, ZAP_MAXNAMELEN_NEW +2);
257 return (error);
258 }
259
260 int
zfs_obj_to_path(objset_t * osp,uint64_t obj,char * buf,int len)261 zfs_obj_to_path(objset_t *osp, uint64_t obj, char *buf, int len)
262 {
263 sa_attr_type_t *sa_table;
264 sa_handle_t *hdl;
265 dmu_buf_t *db;
266 int error;
267
268 error = zfs_sa_setup(osp, &sa_table);
269 if (error != 0)
270 return (error);
271
272 error = zfs_grab_sa_handle(osp, obj, &hdl, &db, FTAG);
273 if (error != 0)
274 return (error);
275
276 error = zfs_obj_to_path_impl(osp, obj, hdl, sa_table, buf, len);
277
278 zfs_release_sa_handle(hdl, db, FTAG);
279 return (error);
280 }
281
282 int
zfs_obj_to_stats(objset_t * osp,uint64_t obj,zfs_stat_t * sb,char * buf,int len)283 zfs_obj_to_stats(objset_t *osp, uint64_t obj, zfs_stat_t *sb,
284 char *buf, int len)
285 {
286 char *path = buf + len - 1;
287 sa_attr_type_t *sa_table;
288 sa_handle_t *hdl;
289 dmu_buf_t *db;
290 int error;
291
292 *path = '\0';
293
294 error = zfs_sa_setup(osp, &sa_table);
295 if (error != 0)
296 return (error);
297
298 error = zfs_grab_sa_handle(osp, obj, &hdl, &db, FTAG);
299 if (error != 0)
300 return (error);
301
302 error = zfs_obj_to_stats_impl(hdl, sa_table, sb);
303 if (error != 0) {
304 zfs_release_sa_handle(hdl, db, FTAG);
305 return (error);
306 }
307
308 error = zfs_obj_to_path_impl(osp, obj, hdl, sa_table, buf, len);
309
310 zfs_release_sa_handle(hdl, db, FTAG);
311 return (error);
312 }
313
314 /*
315 * Read a property stored within the master node.
316 */
317 int
zfs_get_zplprop(objset_t * os,zfs_prop_t prop,uint64_t * value)318 zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
319 {
320 uint64_t *cached_copy = NULL;
321
322 /*
323 * Figure out where in the objset_t the cached copy would live, if it
324 * is available for the requested property.
325 */
326 if (os != NULL) {
327 switch (prop) {
328 case ZFS_PROP_VERSION:
329 cached_copy = &os->os_version;
330 break;
331 case ZFS_PROP_NORMALIZE:
332 cached_copy = &os->os_normalization;
333 break;
334 case ZFS_PROP_UTF8ONLY:
335 cached_copy = &os->os_utf8only;
336 break;
337 case ZFS_PROP_CASE:
338 cached_copy = &os->os_casesensitivity;
339 break;
340 default:
341 break;
342 }
343 }
344 if (cached_copy != NULL && *cached_copy != OBJSET_PROP_UNINITIALIZED) {
345 *value = *cached_copy;
346 return (0);
347 }
348
349 /*
350 * If the property wasn't cached, look up the file system's value for
351 * the property. For the version property, we look up a slightly
352 * different string.
353 */
354 const char *pname;
355 int error = ENOENT;
356 if (prop == ZFS_PROP_VERSION)
357 pname = ZPL_VERSION_STR;
358 else
359 pname = zfs_prop_to_name(prop);
360
361 if (os != NULL) {
362 ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS);
363 error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value);
364 }
365
366 if (error == ENOENT) {
367 /* No value set, use the default value */
368 switch (prop) {
369 case ZFS_PROP_VERSION:
370 *value = ZPL_VERSION;
371 break;
372 case ZFS_PROP_NORMALIZE:
373 case ZFS_PROP_UTF8ONLY:
374 *value = 0;
375 break;
376 case ZFS_PROP_CASE:
377 *value = ZFS_CASE_SENSITIVE;
378 break;
379 case ZFS_PROP_ACLTYPE:
380 #ifdef __FreeBSD__
381 *value = ZFS_ACLTYPE_NFSV4;
382 #else
383 *value = ZFS_ACLTYPE_OFF;
384 #endif
385 break;
386 default:
387 return (error);
388 }
389 error = 0;
390 }
391
392 /*
393 * If one of the methods for getting the property value above worked,
394 * copy it into the objset_t's cache.
395 */
396 if (error == 0 && cached_copy != NULL) {
397 *cached_copy = *value;
398 }
399
400 return (error);
401 }
402