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  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <errno.h>
27 #include <unistd.h>
28 #include <strings.h>
29 #include <sys/fs_reparse.h>
30 #include <smbsrv/libsmb.h>
31 
32 #include <syslog.h>
33 
34 static int smb_reparse_init(const char *, nvlist_t **);
35 static void smb_reparse_free(nvlist_t *);
36 static int smb_reparse_set(const char *, nvlist_t *);
37 
38 /*
39  * Checks the status of the object specified by 'path'
40  *
41  * Returns 0 and fills 'stat' with the proper status on
42  * success, otherwise returns an error code.
43  */
44 int
smb_reparse_stat(const char * path,uint32_t * stat)45 smb_reparse_stat(const char *path, uint32_t *stat)
46 {
47 	struct stat statbuf;
48 	char symbuf[MAXREPARSELEN];
49 	int rptaglen;
50 
51 	if (lstat(path, &statbuf) != 0) {
52 		if (errno == ENOENT) {
53 			*stat = SMB_REPARSE_NOTFOUND;
54 			return (0);
55 		}
56 		return (errno);
57 	}
58 
59 	if ((statbuf.st_mode & S_IFMT) != S_IFLNK) {
60 		*stat = SMB_REPARSE_NOTREPARSE;
61 		return (0);
62 	}
63 
64 	bzero(symbuf, MAXREPARSELEN);
65 	if (readlink(path, symbuf, MAXREPARSELEN) == -1)
66 		return (errno);
67 
68 	rptaglen = strlen(FS_REPARSE_TAG_STR);
69 	if (strncmp(symbuf, FS_REPARSE_TAG_STR, rptaglen) != 0)
70 		*stat = SMB_REPARSE_NOTREPARSE;
71 	else
72 		*stat = SMB_REPARSE_ISREPARSE;
73 
74 	return (0);
75 }
76 
77 /*
78  * If the reparse point specified by the path already exists
79  * it is updated by given service type and its data. Update means
80  * that if such service type does not already exist, it is added
81  * otherwise it is overwritten by given data.
82  *
83  * If the reparse point does not exist, one is created with given
84  * service type and its data.
85  */
86 int
smb_reparse_svcadd(const char * path,const char * svctype,const char * svcdata)87 smb_reparse_svcadd(const char *path, const char *svctype, const char *svcdata)
88 {
89 	nvlist_t *nvl;
90 	int rc;
91 
92 	if ((rc = smb_reparse_init(path, &nvl)) != 0)
93 		return (rc);
94 
95 	if ((rc = reparse_add(nvl, svctype, svcdata)) != 0) {
96 		smb_reparse_free(nvl);
97 		return (rc);
98 	}
99 
100 	rc = smb_reparse_set(path, nvl);
101 	smb_reparse_free(nvl);
102 
103 	return (rc);
104 }
105 
106 /*
107  * Removes the entry for the given service type from the
108  * specified reparse point. If there is no service entry
109  * left, the reparse point object will be deleted.
110  */
111 int
smb_reparse_svcdel(const char * path,const char * svctype)112 smb_reparse_svcdel(const char *path, const char *svctype)
113 {
114 	nvlist_t *nvl;
115 	int rc;
116 
117 	if ((rc = smb_reparse_init(path, &nvl)) != 0)
118 		return (rc);
119 
120 	if ((rc = reparse_remove(nvl, svctype)) != 0) {
121 		smb_reparse_free(nvl);
122 		return (rc);
123 	}
124 
125 	if (nvlist_next_nvpair(nvl, NULL) == NULL) {
126 		/* list is empty remove the object */
127 		rc = reparse_delete(path);
128 		if ((rc != 0) && (rc == ENOENT))
129 			rc = 0;
130 	} else {
131 		rc = smb_reparse_set(path, nvl);
132 	}
133 
134 	smb_reparse_free(nvl);
135 	return (rc);
136 }
137 
138 /*
139  * Obtains data of the given service type from the specified
140  * reparse point. Function allocates the memory needed to hold
141  * the service data so the caller must free this memory by
142  * calling free().
143  *
144  * If 'svcdata' is NULL, successful return means that the reparse
145  * point contains a record for the given service type.
146  */
147 int
smb_reparse_svcget(const char * path,const char * svctype,char ** svcdata)148 smb_reparse_svcget(const char *path, const char *svctype, char **svcdata)
149 {
150 	nvlist_t *nvl;
151 	nvpair_t *nvp;
152 	char *stype, *sdata;
153 	int rc;
154 
155 	if ((rc = smb_reparse_init(path, &nvl)) != 0)
156 		return (rc);
157 
158 	rc = ENODATA;
159 	nvp = nvlist_next_nvpair(nvl, NULL);
160 
161 	while (nvp != NULL) {
162 		stype = nvpair_name(nvp);
163 
164 		if ((stype != NULL) && (strcasecmp(stype, svctype) == 0)) {
165 			if ((rc = nvpair_value_string(nvp, &sdata)) != 0)
166 				break;
167 
168 			if (svcdata != NULL) {
169 				if ((*svcdata = strdup(sdata)) == NULL)
170 					rc = ENOMEM;
171 			}
172 
173 			rc = 0;
174 			break;
175 		}
176 		nvp = nvlist_next_nvpair(nvl, nvp);
177 	}
178 
179 	smb_reparse_free(nvl);
180 	return (rc);
181 }
182 
183 /*
184  * Initializes the given nvpair list.
185  *
186  * This function assumes that the object specified by this path
187  * is a reparse point, so it does not do any verification.
188  *
189  * If specified reparse point does not exist the function
190  * returns successfully with an empty nvpair list.
191  *
192  * If the object exists and readlink is successful then nvpair
193  * list is polulated with the reparse service information, otherwise
194  * an error code is returned.
195  */
196 static int
smb_reparse_init(const char * path,nvlist_t ** nvl)197 smb_reparse_init(const char *path, nvlist_t **nvl)
198 {
199 	char rp_data[MAXREPARSELEN];
200 	int rc;
201 
202 	if ((*nvl = reparse_init()) == NULL)
203 		return (ENOMEM);
204 
205 	bzero(rp_data, MAXREPARSELEN);
206 	if ((rc = readlink(path, rp_data, MAXREPARSELEN)) == -1) {
207 		if (errno == ENOENT)
208 			return (0);
209 
210 		reparse_free(*nvl);
211 		return (errno);
212 	}
213 
214 	if ((rc = reparse_parse(rp_data, *nvl)) != 0) {
215 		reparse_free(*nvl);
216 		return (rc);
217 	}
218 
219 	return (0);
220 }
221 
222 /*
223  * Frees given nvlist
224  */
225 static void
smb_reparse_free(nvlist_t * nvl)226 smb_reparse_free(nvlist_t *nvl)
227 {
228 	reparse_free(nvl);
229 }
230 
231 /*
232  * Create a reparse point with given services in the passed
233  * nvlist. If the reparse point already exists, it will be
234  * deleted and a new one with the given data is created.
235  */
236 static int
smb_reparse_set(const char * path,nvlist_t * nvl)237 smb_reparse_set(const char *path, nvlist_t *nvl)
238 {
239 	char *rp_data;
240 	int rc;
241 
242 	if ((rc = reparse_unparse(nvl, &rp_data)) != 0)
243 		return (rc);
244 
245 	rc = reparse_delete(path);
246 	if ((rc != 0) && (rc != ENOENT)) {
247 		free(rp_data);
248 		return (rc);
249 	}
250 
251 	rc = reparse_create(path, rp_data);
252 	free(rp_data);
253 
254 	return (rc);
255 }
256