xref: /illumos-gate/usr/src/cmd/svr4pkg/pkginstall/cppath.c (revision e4d060fb4c00d44cd578713eb9a921f594b733b8)
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 2005 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 /*
32  * System includes
33  */
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <utime.h>
40 #include <locale.h>
41 #include <libintl.h>
42 #include <pkglocs.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 
48 /*
49  * consolidation pkg command library includes
50  */
51 
52 #include <pkglib.h>
53 
54 /*
55  * local pkg command library includes
56  */
57 
58 #include "libadm.h"
59 #include "libinst.h"
60 #include "install.h"
61 #include "messages.h"
62 #include "pkginstall.h"
63 
64 /*
65  * forward declarations
66  */
67 
68 static int	write_file(char **r_linknam, int a_ctrl, mode_t a_mode,
69 			char *a_file);
70 static int	create_path(int a_ctrl, char *a_file);
71 
72 /*
73  * Name:	cppath
74  * Description:	copy a path object (install new file on system)
75  * Arguments:
76  *    - a_cntrl - determine how the destination file mode is set:
77  *	|= MODE_0666 - force mode to 0666
78  *      |= MODE_SET - mode is a_mode (no mask SET?ID bits)
79  *      |= MODE_SRC - mode from source file (mask SET?ID bits)
80  *      |= DIR_DISPLAY - display "%s <implied directory>" if directory created
81  *    - a_srcPath - path to source to copy
82  *    - a_dstPath - path to copy source to
83  *    - a_mode - mode to set a_dstpath to (mode controlled by a_ctrl)
84  * Returns:	int
85  *	== 0 - success
86  *	!= 0 - failure
87  */
88 
89 int
90 cppath(int a_ctrl, char *a_srcPath, char *a_dstPath, mode_t a_mode)
91 {
92 	char		*linknam = (char *)NULL;
93 	int		dstFd;
94 	int		len;
95 	int		srcFd;
96 	long		status;
97 	struct stat	srcStatbuf;
98 	struct utimbuf	times;
99 
100 	/* entry debugging info */
101 
102 	echoDebug(DBG_CPPATH_ENTRY, a_ctrl, a_mode, a_srcPath, a_dstPath);
103 
104 	/* open source file for reading */
105 
106 	srcFd = open(a_srcPath, O_RDONLY);
107 	if (srcFd < 0) {
108 		progerr(ERR_OPEN_READ, a_srcPath,
109 				errno, strerror(errno));
110 		return (1);
111 	}
112 
113 	/* obtain file status of source file */
114 
115 	if (fstat(srcFd, &srcStatbuf) != 0) {
116 		progerr(ERR_FSTAT, srcFd, a_srcPath, errno, strerror(errno));
117 		(void) close(srcFd);
118 		return (1);
119 	}
120 
121 	/*
122 	 * Determine the permissions mode for the destination:
123 	 * - if MODE_SET is specified:
124 	 * --> use a_mode (do not mask off any portion)
125 	 * --> If a_mode is unknown (? in the pkgmap), then the file gets
126 	 * --> installed with the default 0644 mode
127 	 * - if MODE_SRC is specified:
128 	 * --> use the mode of the source (srcStatbuf.st_mode) but mask off all
129 	 * --> non-access mode bits (remove SET?UID bits)
130 	 * - otherwise:
131 	 * --> use 0666
132 	 */
133 
134 	if (a_ctrl & MODE_SET) {
135 		mode_t	usemode;
136 
137 		usemode = (a_mode ^ BADMODE) ? a_mode : 0644;
138 		if (a_mode != usemode && usemode == 0644) {
139 			logerr(WRN_DEF_MODE, a_dstPath);
140 			a_mode = usemode;
141 		}
142 	} else if (a_ctrl & MODE_SRC) {
143 		a_mode = (srcStatbuf.st_mode & S_IAMB);
144 	} else {
145 		a_mode = 0666;
146 	}
147 
148 	/*
149 	 * Get fd of newly created destination file or, if this
150 	 * is an overwrite,  a temporary file (linknam).
151 	 */
152 
153 	dstFd = write_file(&linknam, a_ctrl, a_mode, a_dstPath);
154 	if (dstFd < 0) {
155 		(void) close(srcFd);
156 		return (1);
157 	}
158 
159 	/*
160 	 * source and target files are open: copy data
161 	 */
162 
163 	status = copyFile(srcFd, dstFd, a_srcPath, a_dstPath, &srcStatbuf, 0);
164 
165 	(void) close(srcFd);
166 	(void) close(dstFd);
167 
168 	if (status != 0) {
169 		progerr(ERR_INPUT, a_srcPath, errno, strerror(errno));
170 		if (linknam) {
171 			(void) remove(linknam);
172 		}
173 		return (1);
174 	}
175 
176 	/*
177 	 * If this is an overwrite, rename temp over original
178 	 */
179 
180 	if ((linknam != (char *)NULL) && (rename(linknam, a_dstPath) != 0)) {
181 		FILE	*logfp = (FILE *)NULL;
182 		char	busylog[PATH_MAX];
183 
184 		/* output log message if busy else program error */
185 
186 		if (errno == ETXTBSY) {
187 			logerr(MSG_PROCMV, linknam);
188 		} else {
189 			progerr(ERR_OUTPUT_WRITING, a_dstPath, errno,
190 				strerror(errno));
191 		}
192 
193 		(void) remove(linknam);
194 
195 		/* open the log file and append log entry */
196 
197 		len = snprintf(busylog, sizeof (busylog),
198 				"%s/textbusy", get_PKGADM());
199 		if (len > sizeof (busylog)) {
200 			progerr(ERR_CREATE_PATH_2, get_PKGADM(),
201 				"textbusy");
202 		} else {
203 			logfp = fopen(busylog, "a");
204 			if (logfp == NULL) {
205 				progerr(ERR_LOG, busylog, errno,
206 					strerror(errno));
207 			} else {
208 				(void) fprintf(logfp, "%s\n", linknam);
209 				(void) fclose(logfp);
210 			}
211 		}
212 	}
213 
214 	/* set access/modification times for target */
215 
216 	times.actime = srcStatbuf.st_atime;
217 	times.modtime = srcStatbuf.st_mtime;
218 
219 	if (utime(a_dstPath, &times) != 0) {
220 		progerr(ERR_MODTIM, a_dstPath, errno, strerror(errno));
221 		return (1);
222 	}
223 
224 	/* success! */
225 
226 	return (0);
227 }
228 
229 /*
230  * This function creates all of the directory components of the specified path.
231  */
232 static int
233 create_path(int a_ctrl, char *a_file)
234 {
235 	char	*pt;
236 	int	found = 0;
237 
238 	for (pt = a_file; *pt; pt++) {
239 		/* continue if not at path separator or at start of path */
240 
241 		if ((*pt != '/') || (pt == a_file)) {
242 			continue;
243 		}
244 
245 		/* at '/' - terminate path at current entry */
246 
247 		*pt = '\0';
248 
249 		/* continue if path element exists */
250 
251 		if (access(a_file, F_OK) == 0) {
252 			*pt = '/';
253 			continue;
254 		}
255 
256 		/* create directory in path */
257 
258 		if (mkdir(a_file, 0755)) {
259 			progerr(ERR_MAKE_DIR, a_file, errno, strerror(errno));
260 			*pt = '/';
261 			return (1);
262 		}
263 
264 		/* display 'implied directory created' message */
265 
266 		if (a_ctrl & DIR_DISPLAY) {
267 			echo(MSG_IMPDIR, a_file);
268 		}
269 
270 		found++;
271 
272 		*pt = '/';
273 	}
274 
275 	return (!found);
276 }
277 
278 /*
279  * Name:	write_file
280  * Description:	creates a new destination file if the file does not already
281  *		exist; otherwise, creates a temporary file and places a
282  *		pointer to the temporary file name in 'r_linknam'.
283  * Arguments:	r_linknam - pointer to (char*) where name of temporary file
284  *			created is returned
285  *		a_ctrl - determine if the destination file name is displayed:
286  *		     |= DIR_DISPLAY - display "%s <implied directory>"
287  *			if directory created
288  *		a_mode - permissions mode to set a_file to
289  *		a_file - name of destination file to open
290  * Returns:	int
291  *			success - file descriptor of the file it opened.
292  *			failure - returns -1
293  */
294 
295 static int
296 write_file(char **r_linknam, int a_ctrl, mode_t a_mode, char *a_file)
297 {
298 	int		len;
299 	int		fd = -1;
300 	static char	loc_link[PATH_MAX];
301 
302 	/* entry debugging */
303 
304 	echoDebug(DBG_WRITEFILE_ENTRY, a_ctrl, a_mode, a_file);
305 
306 	/* reset pointer to returned 'temporary file name' */
307 
308 	*r_linknam = (char *)NULL;
309 
310 	/*
311 	 * If we are overwriting an existing file, arrange to replace
312 	 * it transparently.
313 	 */
314 
315 	if (access(a_file, F_OK) == 0) {
316 		/*
317 		 * link the file to be copied to a temporary name in case
318 		 * it is executing or it is being written/used (e.g., a shell
319 		 * script currently being executed
320 		 */
321 
322 		if (!RELATIVE(a_file)) {
323 			len = snprintf(loc_link, sizeof (loc_link),
324 					"%sXXXXXX", a_file);
325 			if (len > sizeof (loc_link)) {
326 				progerr(ERR_CREATE_PATH_2, a_file, "XXXXXX");
327 			}
328 		} else {
329 			logerr(WRN_RELATIVE, a_file);
330 			len = snprintf(loc_link, sizeof (loc_link),
331 					"./%sXXXXXX", a_file);
332 			if (len > sizeof (loc_link)) {
333 				progerr(ERR_CREATE_PATH_3, "./", a_file,
334 					"XXXXXX");
335 			}
336 		}
337 
338 		/* create and open temporary file */
339 
340 		fd = mkstemp(loc_link);
341 		if (fd == -1) {
342 			progerr(ERR_MKTEMP, loc_link, errno, strerror(errno));
343 			return (-1);
344 		}
345 
346 		/* remember name of temporary file */
347 
348 		*r_linknam = loc_link;
349 
350 		/* make sure temporary file has correct mode */
351 
352 		if (fchmod(fd, a_mode) < 0) {
353 			progerr(ERR_FCHMOD, loc_link, a_mode, errno,
354 				strerror(errno));
355 		}
356 
357 		return (fd);
358 	}
359 
360 	/*
361 	 * We are not overwriting an existing file, create a new one directly.
362 	 */
363 
364 	fd = open(a_file, O_WRONLY | O_CREAT | O_TRUNC, a_mode);
365 	if (fd == -1) {
366 		if (create_path(a_ctrl, a_file) == 0) {
367 			fd = open(a_file, O_WRONLY | O_CREAT | O_TRUNC, a_mode);
368 		}
369 	}
370 
371 	if (fd == -1) {
372 		progerr(ERR_OPEN_WRITE, a_file, errno, strerror(errno));
373 	}
374 
375 	return (fd);
376 }
377