xref: /illumos-gate/usr/src/lib/libpkg/common/isdir.c (revision 3ed623140e27064f81020d9d47f9fb17489d1190)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <archives.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <limits.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include "pkglocale.h"
42 #include "pkglibmsgs.h"
43 #include "pkglib.h"
44 
45 /*
46  * Defines for cpio/compression checks.
47  */
48 #define	BIT_MASK		0x1f
49 #define	BLOCK_MASK		0x80
50 
51 #define	MASK_CK(x, y)	(((x) & (y)) == (y))
52 #define	ISCOMPCPIO	((unsigned char) cm.c_mag[0] == m_h[0] && \
53 			(unsigned char) cm.c_mag[1] == m_h[1] && \
54 			(MASK_CK((unsigned char) cm.c_mag[2], BLOCK_MASK) || \
55 			MASK_CK((unsigned char) cm.c_mag[2], BIT_MASK)))
56 
57 #define	ISCPIO		(cm.b_mag != CMN_BIN && \
58 			(strcmp(cm.c_mag, CMS_ASC) == 0) && \
59 			(strcmp(cm.c_mag, CMS_CHR) == 0) && \
60 			(strcmp(cm.c_mag, CMS_CRC) == 0))
61 
62 /* location of distributed file system types database */
63 
64 #define	REMOTE_FS_DBFILE	"/etc/dfs/fstypes"
65 
66 /* character array used to hold dfs types database contents */
67 
68 static long		numRemoteFstypes = -1;
69 static char		**remoteFstypes = (char **)NULL;
70 
71 /* forward declarations */
72 
73 static void _InitRemoteFstypes(void);
74 
75 int isFdRemote(int a_fd);
76 int isPathRemote(char *a_path);
77 int isFstypeRemote(char *a_fstype);
78 int isdir(char *path);
79 int isfile(char *dir, char *file);
80 int iscpio(char *path, int *iscomp);
81 int eval_ftype(char *path, char ftype, char *myftype);
82 
83 /*
84  * Name:	isdir
85  * Description:	determine if specified path exists and is a directory
86  * Arguments:	path - pointer to string representing the path to verify
87  * returns: 0 - directory exists
88  *	    1 - directory does not exist or is not a directory
89  * NOTE:	errno is set appropriately
90  */
91 
92 int
93 isdir(char *path)
94 {
95 	struct stat statbuf;
96 
97 	/* return error if path does not exist */
98 
99 	if (stat(path, &statbuf) != 0) {
100 		return (1);
101 	}
102 
103 	/* return error if path is not a directory */
104 
105 	if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
106 		errno = ENOTDIR;
107 		return (1);
108 	}
109 
110 	return (0);
111 }
112 
113 /*
114  * Name:	isfile
115  * Description:	determine if specified path exists and is a directory
116  * Arguments:	dir - pointer to string representing the directory where
117  *			the file is located
118  *			== NULL - use "file" argument only
119  *		file - pointer to string representing the file to verify
120  * Returns:	0 - success - file exists
121  *		1 - failure - file does not exist OR is not a file
122  * NOTE:	errno is set appropriately
123  */
124 
125 int
126 isfile(char *dir, char *file)
127 {
128 	struct stat statbuf;
129 	char	path[PATH_MAX];
130 
131 	/* construct full path if directory specified */
132 
133 	if (dir) {
134 		(void) snprintf(path, sizeof (path), "%s/%s", dir, file);
135 		file = path;
136 	}
137 
138 	/* return error if path does not exist */
139 
140 	if (stat(file, &statbuf) != 0) {
141 		return (1);
142 	}
143 
144 	/* return error if path is a directory */
145 
146 	if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
147 		errno = EISDIR;
148 		return (1);
149 	}
150 
151 	/* return error if path is not a file */
152 
153 	if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
154 		errno = EINVAL;
155 		return (1);
156 	}
157 
158 	return (0);
159 }
160 
161 int
162 iscpio(char *path, int *iscomp)
163 {
164 	/*
165 	 * Compressed File Header.
166 	 */
167 	unsigned char m_h[] = { "\037\235" };		/* 1F 9D */
168 
169 	static union {
170 		short int	b_mag;
171 		char		c_mag[CMS_LEN];
172 	}	cm;
173 
174 	struct stat	statb;
175 	int		fd;
176 
177 
178 	*iscomp = 0;
179 
180 	if ((fd = open(path, O_RDONLY, 0)) == -1) {
181 		if (errno != ENOENT) {
182 			perror("");
183 			(void) fprintf(stderr, pkg_gt(ERR_ISCPIO_OPEN), path);
184 		}
185 		return (0);
186 	} else {
187 		if (fstat(fd, &statb) == -1) {
188 			perror("");
189 			(void) fprintf(stderr, pkg_gt(ERR_ISCPIO_FSTAT), path);
190 			(void) close(fd);
191 			return (0);
192 		} else {
193 			if (S_ISREG(statb.st_mode)) {	/* Must be a file */
194 				if (read(fd, cm.c_mag, sizeof (cm.c_mag)) !=
195 				    sizeof (cm.c_mag)) {
196 					perror("");
197 					(void) fprintf(stderr,
198 					    pkg_gt(ERR_ISCPIO_READ), path);
199 					(void) close(fd);
200 					return (0);
201 				}
202 				/*
203 				 * Try to determine if the file is a compressed
204 				 * file, if that fails, try to determine if it
205 				 * is a cpio archive, if that fails, then we
206 				 * fail!
207 				 */
208 				if (ISCOMPCPIO) {
209 					*iscomp = 1;
210 					(void) close(fd);
211 					return (1);
212 				} else if (ISCPIO) {
213 					(void) fprintf(stderr,
214 					    pkg_gt(ERR_ISCPIO_NOCPIO),
215 					    path);
216 					(void) close(fd);
217 					return (0);
218 				}
219 				(void) close(fd);
220 				return (1);
221 			} else {
222 				(void) close(fd);
223 				return (0);
224 			}
225 		}
226 	}
227 }
228 
229 /*
230  * Name:	isPathRemote
231  * Description:	determine if a path object is local or remote
232  * Arguments:	a_path - [RO, *RO] - (char *)
233  *			Pointer to string representing the path to check
234  * Returns:	int
235  *			1 - the path is remote
236  *			0 - the path is local to this system
237  *			-1 - cannot determine if path is remote or local
238  */
239 
240 int
241 isPathRemote(char *a_path)
242 {
243 	int		r;
244 	struct stat	statbuf;
245 
246 	r = lstat(a_path, &statbuf);
247 	if (r < 0) {
248 		return (-1);
249 	}
250 
251 	return (isFstypeRemote(statbuf.st_fstype));
252 }
253 
254 /*
255  * Name:	isFdRemote
256  * Description:	determine if an open file is local or remote
257  * Arguments:	a_fd - [RO, *RO] - (int)
258  *			Integer representing open file to check
259  * Returns:	int
260  *			1 - the path is remote
261  *			0 - the path is local to this system
262  *			-1 - cannot determine if path is remote or local
263  */
264 
265 int
266 isFdRemote(int a_fd)
267 {
268 	int		r;
269 	struct stat	statbuf;
270 
271 	r = fstat(a_fd, &statbuf);
272 	if (r < 0) {
273 		return (-1);
274 	}
275 
276 	return (isFstypeRemote(statbuf.st_fstype));
277 }
278 
279 /*
280  * Name:	isFstypeRemote
281  * Description:	determine if a file system type is remote (distributed)
282  * Arguments:	a_fstype - [RO, *RO] - (char *)
283  *			Pointer to string representing the file system type
284  *			to check
285  * Returns:	int
286  *			1 - the file system type is remote
287  *			0 - the file system type is local to this system
288  */
289 
290 int
291 isFstypeRemote(char *a_fstype)
292 {
293 	int	i;
294 
295 	/* initialize the list if it is not yet initialized */
296 
297 	_InitRemoteFstypes();
298 
299 	/* scan the list looking for the specified type */
300 
301 	for (i = 0; i < numRemoteFstypes; i++) {
302 		if (strcmp(remoteFstypes[i], a_fstype) == 0) {
303 			return (1);
304 		}
305 	}
306 
307 	/* type not found in remote file system type list - is not remote */
308 
309 	return (0);
310 }
311 
312 /*
313  * Name:	_InitRemoteFstypes
314  * Description:	initialize table of remote file system type names
315  * Arguments:	none
316  * Returns:	none
317  * Side Effects:
318  *	- The global array "(char **)remoteFstypes" is set to the
319  *	  address of an array of string pointers, each of which represents
320  *	  a single remote file system type
321  *	- The global variable "(long) numRemoteFstypes" is set to the total
322  *	  number of remote file system type strings (names) that are
323  *	  contained in the "remoteFstypes" global array.
324  *	- numRemoteFstypes is initialized to "-1" before any attempt has been
325  *	  made to read the remote file system type name database.
326  */
327 static void
328 _InitRemoteFstypes(void)
329 {
330 	FILE    *fp;
331 	char    line_buf[LINE_MAX];
332 
333 	/* return if already initialized */
334 
335 	if (numRemoteFstypes > 0) {
336 		return;
337 	}
338 
339 	/* if list is uninitialized, start with zero */
340 
341 	if (numRemoteFstypes == -1) {
342 		numRemoteFstypes = 0;
343 	}
344 
345 	/* open the remote file system type database file */
346 
347 	if ((fp = fopen(REMOTE_FS_DBFILE, "r")) == NULL) {
348 		/* no remote type database: use predefined remote types */
349 		remoteFstypes = (char **)realloc(remoteFstypes,
350 					sizeof (char *) * (numRemoteFstypes+3));
351 		remoteFstypes[numRemoteFstypes++] = "nfs";	/* +1 */
352 		remoteFstypes[numRemoteFstypes++] = "autofs";	/* +2 */
353 		remoteFstypes[numRemoteFstypes++] = "cachefs";	/* +3 */
354 		return;
355 	}
356 
357 	/*
358 	 * Read the remote file system type database; from fstypes(4):
359 	 *
360 	 * fstypes resides in directory /etc/dfs and lists distributed file
361 	 * system utilities packages installed on the system. For each installed
362 	 * distributed file system type, there is a line that begins with the
363 	 * file system type name (for example, ``nfs''), followed by white space
364 	 * and descriptive text.
365 	 *
366 	 * Lines will look at lot like this:
367 	 *
368 	 *	nfs NFS Utilities
369 	 *	autofs AUTOFS Utilities
370 	 *	cachefs CACHEFS Utilities
371 	 */
372 
373 	while (fgets(line_buf, sizeof (line_buf), fp) != NULL) {
374 		char		buf[LINE_MAX];
375 		static char	format[128] = {'\0'};
376 
377 		if (format[0] == '\0') {
378 			/* create bounded format: %ns */
379 			(void) snprintf(format, sizeof (format),
380 				"%%%ds", sizeof (buf)-1);
381 		}
382 
383 		(void) sscanf(line_buf, format, buf);
384 
385 		remoteFstypes = realloc(remoteFstypes,
386 					sizeof (char *) * (numRemoteFstypes+1));
387 		remoteFstypes[numRemoteFstypes++] = strdup(buf);
388 	}
389 
390 	/* close database file and return */
391 
392 	(void) fclose(fp);
393 }
394 
395 /*
396  * Name:	eval_ftype
397  * Description: Evaluate the target's file type
398  * Arguments:	path      - Path on filesystem
399  *		ftype     - Type to be changed to
400  *		myftype   - Address into which current
401  *			    type of target will be stored
402  * Returns:	int
403  *			0	 - Success
404  *			VE_EXIST - Path does not exist
405  *			VE_FTYPE - Path file type is not recognized,
406  *				   is not supported,
407  *				   or is not what is expected
408  */
409 int
410 eval_ftype(char *path, char ftype, char *myftype)
411 {
412 	struct stat status;
413 	int retcode = 0;
414 	int statError = 0;
415 
416 	/* If we are to process symlinks the old way then we follow the link */
417 	if (nonABI_symlinks()) {
418 		if ((ftype == 's') ? lstat(path, &status) :
419 		    stat(path, &status)) {
420 			(void) reperr(pkg_gt(ERR_EXIST));
421 			retcode = VE_EXIST;
422 			*myftype = '?';
423 			statError++;
424 		}
425 	/* If not then we inspect the target of the link */
426 	} else {
427 		if (lstat(path, &status) == -1) {
428 			reperr(pkg_gt(ERR_EXIST));
429 			retcode = VE_EXIST;
430 			*myftype = '?';
431 			statError++;
432 		}
433 	}
434 	if (!statError) {
435 		/* determining actual type of existing object */
436 		switch (status.st_mode & S_IFMT) {
437 			case S_IFLNK:
438 					*myftype = 's';
439 					break;
440 
441 			case S_IFIFO:
442 					*myftype = 'p';
443 					break;
444 
445 			case S_IFCHR:
446 					*myftype = 'c';
447 					break;
448 
449 			case S_IFDIR:
450 					*myftype = 'd';
451 					break;
452 
453 			case S_IFBLK:
454 					*myftype = 'b';
455 					break;
456 
457 			case S_IFREG:
458 			case 0:
459 					*myftype = 'f';
460 					break;
461 
462 			case S_IFDOOR:
463 					*myftype = 'D';
464 					break;
465 
466 			default:
467 					*myftype = '?';
468 					return (VE_FTYPE);
469 		}
470 		retcode = 0;
471 	}
472 	return (retcode);
473 }
474