1 /*
2  * sysfs_utils.c
3  *
4  * System utility functions for libsysfs
5  *
6  * Copyright (C) IBM Corp. 2003-2005
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23 #include "libsysfs.h"
24 #include "sysfs.h"
25 
26 /**
27  * sysfs_remove_trailing_slash: Removes any trailing '/' in the given path
28  * @path: Path to look for the trailing '/'
29  * Returns 0 on success 1 on error
30  */
sysfs_remove_trailing_slash(char * path)31 int sysfs_remove_trailing_slash(char *path)
32 {
33 	size_t len;
34 
35 	if (!path) {
36 		errno = EINVAL;
37 		return 1;
38 	}
39 
40 	len = strlen(path);
41 	while (len > 0 && path[len-1] == '/')
42 		path[--len] = '\0';
43 	return 0;
44 }
45 
46 /*
47  * sysfs_get_mnt_path: Gets the sysfs mount point.
48  * @mnt_path: place to put "sysfs" mount point
49  * @len: size of mnt_path
50  * returns 0 with success and -1 with error.
51  */
sysfs_get_mnt_path(char * mnt_path,size_t len)52 int sysfs_get_mnt_path(char *mnt_path, size_t len)
53 {
54 	static char sysfs_path[SYSFS_PATH_MAX] = "";
55 	const char *sysfs_path_env;
56 
57 	if (len == 0 || mnt_path == NULL)
58 		return -1;
59 
60 	/* evaluate only at the first call */
61 	if (sysfs_path[0] == '\0') {
62 		/* possible overrride of real mount path */
63 		sysfs_path_env = getenv(SYSFS_PATH_ENV);
64 		if (sysfs_path_env != NULL) {
65 			safestrcpymax(mnt_path, sysfs_path_env, len);
66 			sysfs_remove_trailing_slash(mnt_path);
67 			return 0;
68 		}
69 		safestrcpymax(mnt_path, SYSFS_MNT_PATH, len);
70 	}
71 
72 	return 0;
73 }
74 
75 /**
76  * sysfs_get_name_from_path: returns last name from a "/" delimited path
77  * @path: path to get name from
78  * @name: where to put name
79  * @len: size of name
80  */
sysfs_get_name_from_path(const char * path,char * name,size_t len)81 int sysfs_get_name_from_path(const char *path, char *name, size_t len)
82 {
83 	char tmp[SYSFS_PATH_MAX];
84 	char *n = NULL;
85 
86 	if (!path || !name || len == 0) {
87 		errno = EINVAL;
88 		return -1;
89 	}
90 	memset(tmp, 0, SYSFS_PATH_MAX);
91 	safestrcpy(tmp, path);
92 	n = strrchr(tmp, '/');
93 	if (n == NULL) {
94 		errno = EINVAL;
95 		return -1;
96 	}
97 	if (*(n+1) == '\0') {
98 		*n = '\0';
99 		n = strrchr(tmp, '/');
100 		if (n == NULL) {
101 			errno = EINVAL;
102 			return -1;
103 		}
104 	}
105 	n++;
106 	safestrcpymax(name, n, len);
107 	return 0;
108 }
109 
110 /**
111  * sysfs_get_link: returns link source
112  * @path: symbolic link's path
113  * @target: where to put name
114  * @len: size of name
115  */
sysfs_get_link(const char * path,char * target,size_t len)116 int sysfs_get_link(const char *path, char *target, size_t len)
117 {
118 	char devdir[SYSFS_PATH_MAX];
119 	char linkpath[SYSFS_PATH_MAX];
120 	char temp_path[SYSFS_PATH_MAX];
121 	char *d = NULL, *s = NULL;
122 	int slashes = 0, count = 0;
123 
124 	if (!path || !target || len == 0) {
125 		errno = EINVAL;
126 		return -1;
127 	}
128 
129 	memset(devdir, 0, SYSFS_PATH_MAX);
130 	memset(linkpath, 0, SYSFS_PATH_MAX);
131 	memset(temp_path, 0, SYSFS_PATH_MAX);
132 	safestrcpy(devdir, path);
133 
134 	if ((readlink(path, linkpath, SYSFS_PATH_MAX)) < 0) {
135 		return -1;
136 	}
137 	d = linkpath;
138 	/*
139 	 * Three cases here:
140 	 * 1. relative path => format ../..
141 	 * 2. absolute path => format /abcd/efgh
142 	 * 3. relative path _from_ this dir => format abcd/efgh
143 	 */
144 	switch (*d) {
145 		case '.':
146 			/*
147 			 * handle the case where link is of type ./abcd/xxx
148 			 */
149 			safestrcpy(temp_path, devdir);
150 			if (*(d+1) == '/')
151 				d += 2;
152 			else if (*(d+1) == '.')
153 				goto parse_path;
154 			s = strrchr(temp_path, '/');
155 			if (s != NULL) {
156 				*(s+1) = '\0';
157 				safestrcat(temp_path, d);
158 			} else {
159 				safestrcpy(temp_path, d);
160 			}
161 			safestrcpymax(target, temp_path, len);
162 			break;
163 			/*
164 			 * relative path, getting rid of leading "../.."
165 			 */
166 parse_path:
167 			while (*d == '/' || *d == '.') {
168 				if (*d == '/')
169 					slashes++;
170 				d++;
171 			}
172 			d--;
173 			s = &devdir[strlen(devdir)-1];
174 			while (s != NULL && count != (slashes+1)) {
175 				s--;
176 				if (*s == '/')
177 					count++;
178 			}
179 			safestrcpymax(s, d, (SYSFS_PATH_MAX-strlen(devdir)));
180 			safestrcpymax(target, devdir, len);
181 			break;
182 		case '/':
183 			/* absolute path - copy as is */
184 			safestrcpymax(target, linkpath, len);
185 			break;
186 		default:
187 			/* relative path from this directory */
188 			safestrcpy(temp_path, devdir);
189 			s = strrchr(temp_path, '/');
190 			if (s != NULL) {
191 				*(s+1) = '\0';
192 				safestrcat(temp_path, linkpath);
193 			} else {
194 				safestrcpy(temp_path, linkpath);
195 			}
196 			safestrcpymax(target, temp_path, len);
197 	}
198 	return 0;
199 }
200 
201 /**
202  * sysfs_close_list: generic list free routine
203  * @list: dlist to free
204  * Returns nothing
205  */
sysfs_close_list(struct dlist * list)206 void sysfs_close_list(struct dlist *list)
207 {
208 	if (list)
209 		dlist_destroy(list);
210 }
211 
212 /**
213  * sysfs_open_directory_list: gets a list of all directories under "path"
214  * @path: path to read
215  * Returns a dlist of supported names or NULL no directories (errno is set
216  * 	in case of error
217  */
sysfs_open_directory_list(const char * path)218 struct dlist *sysfs_open_directory_list(const char *path)
219 {
220 	if (!path)
221 		return NULL;
222 
223 	return (read_dir_subdirs(path));
224 }
225 
226 /**
227  * sysfs_open_link_list: gets a list of all links under "path"
228  * @path: path to read
229  * Returns a dlist of supported links or NULL no directories (errno is set
230  * 	in case of error
231  */
sysfs_open_link_list(const char * path)232 struct dlist *sysfs_open_link_list(const char *path)
233 {
234 	if (!path)
235 		return NULL;
236 
237 	return (read_dir_links(path));
238 }
239 
240 /**
241  * sysfs_path_is_dir: Check if the path supplied points to a directory
242  * @path: path to validate
243  * Returns 0 if path points to dir, 1 otherwise
244  */
sysfs_path_is_dir(const char * path)245 int sysfs_path_is_dir(const char *path)
246 {
247 	struct stat astats;
248 
249 	if (!path) {
250 		errno = EINVAL;
251 		return 1;
252 	}
253 	if ((lstat(path, &astats)) != 0) {
254 		dprintf("stat() failed\n");
255 		return 1;
256 	}
257 	if (S_ISDIR(astats.st_mode))
258 		return 0;
259 
260 	return 1;
261 }
262 
263 /**
264  * sysfs_path_is_link: Check if the path supplied points to a link
265  * @path: path to validate
266  * Returns 0 if path points to link, 1 otherwise
267  */
sysfs_path_is_link(const char * path)268 int sysfs_path_is_link(const char *path)
269 {
270 	struct stat astats;
271 
272 	if (!path) {
273 		errno = EINVAL;
274 		return 1;
275 	}
276 	if ((lstat(path, &astats)) != 0) {
277 		dprintf("stat() failed\n");
278 		return 1;
279 	}
280 	if (S_ISLNK(astats.st_mode))
281 		return 0;
282 
283 	return 1;
284 }
285 
286 /**
287  * sysfs_path_is_file: Check if the path supplied points to a file
288  * @path: path to validate
289  * Returns 0 if path points to file, 1 otherwise
290  */
sysfs_path_is_file(const char * path)291 int sysfs_path_is_file(const char *path)
292 {
293 	struct stat astats;
294 
295 	if (!path) {
296 		errno = EINVAL;
297 		return 1;
298 	}
299 	if ((lstat(path, &astats)) != 0) {
300 		dprintf("stat() failed\n");
301 		return 1;
302 	}
303 	if (S_ISREG(astats.st_mode))
304 		return 0;
305 
306 	return 1;
307 }
308