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 http://www.opensolaris.org/os/licensing.
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/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include "lint.h"
28#include <string.h>
29#include <stdlib.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <mtlib.h>
33#include <attr.h>
34#include <sys/types.h>
35#include <sys/syscall.h>
36#include <sys/stat.h>
37#include <sys/file.h>
38#include <unistd.h>
39#include <dlfcn.h>
40#include <stdio.h>
41#include <atomic.h>
42
43static int (*nvpacker)(nvlist_t *, char **, size_t *, int, int);
44static int (*nvsize)(nvlist_t *, size_t *, int);
45static int (*nvunpacker)(char *, size_t, nvlist_t **);
46static int (*nvfree)(nvlist_t *);
47static int (*nvlookupint64)(nvlist_t *, const char *, uint64_t *);
48
49static mutex_t attrlock = DEFAULTMUTEX;
50static int initialized;
51
52static char *xattr_view_name[XATTR_VIEW_LAST] = {
53	VIEW_READONLY,
54	VIEW_READWRITE
55};
56
57int
58__openattrdirat(int fd, const char *name)
59{
60	return (syscall(SYS_openat, fd, name, FXATTRDIROPEN, 0));
61}
62
63static int
64attrat_init()
65{
66	void *packer;
67	void *sizer;
68	void *unpacker;
69	void *freer;
70	void *looker;
71
72	if (initialized == 0) {
73		void *handle = dlopen("libnvpair.so.1", RTLD_LAZY);
74
75		if (handle == NULL ||
76		    (packer = dlsym(handle, "nvlist_pack")) == NULL ||
77		    (sizer = dlsym(handle, "nvlist_size")) == NULL ||
78		    (unpacker = dlsym(handle, "nvlist_unpack")) == NULL ||
79		    (freer = dlsym(handle, "nvlist_free")) == NULL ||
80		    (looker = dlsym(handle, "nvlist_lookup_uint64")) == NULL) {
81			if (handle)
82				(void) dlclose(handle);
83			return (-1);
84		}
85
86		lmutex_lock(&attrlock);
87
88		if (initialized != 0) {
89			lmutex_unlock(&attrlock);
90			(void) dlclose(handle);
91			return (0);
92		}
93
94		nvpacker = (int (*)(nvlist_t *, char **, size_t *, int, int))
95		    packer;
96		nvsize = (int (*)(nvlist_t *, size_t *, int))
97		    sizer;
98		nvunpacker = (int (*)(char *, size_t, nvlist_t **))
99		    unpacker;
100		nvfree = (int (*)(nvlist_t *))
101		    freer;
102		nvlookupint64 = (int (*)(nvlist_t *, const char *, uint64_t *))
103		    looker;
104
105		membar_producer();
106		initialized = 1;
107		lmutex_unlock(&attrlock);
108	}
109	return (0);
110}
111
112static int
113attr_nv_pack(nvlist_t *request, void **nv_request, size_t *nv_requestlen)
114{
115	size_t bufsize;
116	char *packbuf = NULL;
117
118	if (nvsize(request, &bufsize, NV_ENCODE_XDR) != 0) {
119		errno = EINVAL;
120		return (-1);
121	}
122
123	packbuf = malloc(bufsize);
124	if (packbuf == NULL)
125		return (-1);
126	if (nvpacker(request, &packbuf, &bufsize, NV_ENCODE_XDR, 0) != 0) {
127		free(packbuf);
128		errno = EINVAL;
129		return (-1);
130	} else {
131		*nv_request = (void *)packbuf;
132		*nv_requestlen = bufsize;
133	}
134	return (0);
135}
136
137static const char *
138view_to_name(xattr_view_t view)
139{
140	if (view >= XATTR_VIEW_LAST || view < 0)
141		return (NULL);
142	return (xattr_view_name[view]);
143}
144
145static int
146xattr_openat(int basefd, xattr_view_t view, int mode)
147{
148	const char *xattrname;
149	int xattrfd;
150	int oflag;
151
152	switch (view) {
153	case XATTR_VIEW_READONLY:
154		oflag = O_RDONLY;
155		break;
156	case XATTR_VIEW_READWRITE:
157		oflag = mode & O_RDWR;
158		break;
159	default:
160		errno = EINVAL;
161		return (-1);
162	}
163	if (mode & O_XATTR)
164		oflag |= O_XATTR;
165
166	xattrname = view_to_name(view);
167	xattrfd = openat(basefd, xattrname, oflag);
168	if (xattrfd < 0)
169		return (xattrfd);
170	/* Don't cache sysattr info (advisory) */
171	(void) directio(xattrfd, DIRECTIO_ON);
172	return (xattrfd);
173}
174
175static int
176cgetattr(int fd, nvlist_t **response)
177{
178	int error;
179	int bytesread;
180	void *nv_response;
181	size_t nv_responselen;
182	struct stat buf;
183
184	if (error = attrat_init())
185		return (error);
186	if ((error = fstat(fd, &buf)) != 0)
187		return (error);
188	nv_responselen = buf.st_size;
189
190	if ((nv_response = malloc(nv_responselen)) == NULL)
191		return (-1);
192	bytesread = read(fd, nv_response, nv_responselen);
193	if (bytesread != nv_responselen) {
194		free(nv_response);
195		errno = EFAULT;
196		return (-1);
197	}
198
199	if (nvunpacker(nv_response, nv_responselen, response)) {
200		free(nv_response);
201		errno = ENOMEM;
202		return (-1);
203	}
204
205	free(nv_response);
206	return (0);
207}
208
209static int
210csetattr(int fd, nvlist_t *request)
211{
212	int error, saveerrno;
213	int byteswritten;
214	void *nv_request;
215	size_t nv_requestlen;
216
217	if (error = attrat_init())
218		return (error);
219
220	if ((error = attr_nv_pack(request, &nv_request, &nv_requestlen)) != 0)
221		return (error);
222
223	byteswritten = write(fd, nv_request, nv_requestlen);
224	if (byteswritten != nv_requestlen) {
225		saveerrno = errno;
226		free(nv_request);
227		errno = saveerrno;
228		return (-1);
229	}
230
231	free(nv_request);
232	return (0);
233}
234
235int
236fgetattr(int basefd, xattr_view_t view, nvlist_t **response)
237{
238	int error, saveerrno, xattrfd;
239
240	if ((xattrfd = xattr_openat(basefd, view, O_XATTR)) < 0)
241		return (xattrfd);
242
243	error = cgetattr(xattrfd, response);
244	saveerrno = errno;
245	(void) close(xattrfd);
246	errno = saveerrno;
247	return (error);
248}
249
250int
251fsetattr(int basefd, xattr_view_t view, nvlist_t *request)
252{
253	int error, saveerrno, xattrfd;
254
255	if ((xattrfd = xattr_openat(basefd, view, O_RDWR | O_XATTR)) < 0)
256		return (xattrfd);
257	error = csetattr(xattrfd, request);
258	saveerrno = errno;
259	(void) close(xattrfd);
260	errno = saveerrno;
261	return (error);
262}
263
264int
265getattrat(int basefd, xattr_view_t view, const char *name, nvlist_t **response)
266{
267	int error, saveerrno, namefd, xattrfd;
268
269	if ((namefd = __openattrdirat(basefd, name)) < 0)
270		return (namefd);
271
272	if ((xattrfd = xattr_openat(namefd, view, 0)) < 0) {
273		saveerrno = errno;
274		(void) close(namefd);
275		errno = saveerrno;
276		return (xattrfd);
277	}
278
279	error = cgetattr(xattrfd, response);
280	saveerrno = errno;
281	(void) close(namefd);
282	(void) close(xattrfd);
283	errno = saveerrno;
284	return (error);
285}
286
287int
288setattrat(int basefd, xattr_view_t view, const char *name, nvlist_t *request)
289{
290	int error, saveerrno, namefd, xattrfd;
291
292	if ((namefd = __openattrdirat(basefd, name)) < 0)
293		return (namefd);
294
295	if ((xattrfd = xattr_openat(namefd, view, O_RDWR)) < 0) {
296		saveerrno = errno;
297		(void) close(namefd);
298		errno = saveerrno;
299		return (xattrfd);
300	}
301
302	error = csetattr(xattrfd, request);
303	saveerrno = errno;
304	(void) close(namefd);
305	(void) close(xattrfd);
306	errno = saveerrno;
307	return (error);
308}
309
310void
311libc_nvlist_free(nvlist_t *nvp)
312{
313	nvfree(nvp);
314}
315
316int
317libc_nvlist_lookup_uint64(nvlist_t *nvp, const char *name, uint64_t *value)
318{
319	return (nvlookupint64(nvp, name, value));
320}
321