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 #include <stdio.h> 33 #include <string.h> 34 #include <errno.h> 35 #include <stdlib.h> 36 #include <unistd.h> 37 #include <libgen.h> 38 #include <fcntl.h> 39 #include <sys/types.h> 40 #include <utime.h> 41 #include <sys/stat.h> 42 #include <locale.h> 43 #include <libintl.h> 44 #include <sys/mman.h> 45 46 /* 47 * consolidation pkg command library includes 48 */ 49 50 #include "pkglib.h" 51 52 /* 53 * local pkg command library includes 54 */ 55 #include "libinst.h" 56 #include "libadm.h" 57 #include "messages.h" 58 59 /* 60 * MAXMAPSIZE controls the largest mapping to use at a time; please refer 61 * to mmap(2) for details of how this size is incremented and rounded; briefly 62 * each mapping request has an additional 16Kb added to it - mappings over 63 * 4Mb will be rounded to a 4Mb boundary - thus if there were 8mb, adding 64 * in the 16Kb overhead the mapping would use another 4Mb-16kb - that is 65 * why there is 16Kb subtracted from the total 66 */ 67 68 #define MAXMAPSIZE (1024*1024*8)-(1024*16) /* map at most 8MB */ 69 #define SMALLFILESIZE (32*1024) /* dont mmap files less than 32kb */ 70 71 /* 72 * Name: copyF 73 * Description: fast copy of file - use mmap()/write() loop if possible 74 * Arguments: char *srcPath - name of source file to copy from 75 * char *dstPath - name of target file to copy to 76 * time_t a_mytime: control setting of access/modification times: 77 * == 0 - replicate source file access/modification times 78 * != 0 - use specified time for access/modification times 79 * Returns: int 80 * == 0 - successful 81 * != 0 - failure 82 */ 83 84 int 85 copyf(char *a_srcPath, char *a_dstPath, time_t a_mytime) 86 { 87 struct stat srcStatbuf; 88 struct utimbuf times; 89 int srcFd; 90 int dstFd; 91 int status; 92 char *pt; 93 94 /* open source file for reading */ 95 96 srcFd = open(a_srcPath, O_RDONLY, 0); 97 if (srcFd < 0) { 98 progerr(ERR_OPEN_READ, a_srcPath, errno, strerror(errno)); 99 return (-1); 100 } 101 102 /* obtain file status of source file */ 103 104 if (fstat(srcFd, &srcStatbuf) != 0) { 105 progerr(ERR_FSTAT, srcFd, a_srcPath, errno, strerror(errno)); 106 (void) close(srcFd); 107 return (-1); 108 } 109 110 /* open target file for writing */ 111 112 dstFd = open(a_dstPath, O_WRONLY | O_TRUNC | O_CREAT, 113 srcStatbuf.st_mode); 114 if (dstFd < 0) { 115 /* create directory structure if missing */ 116 pt = a_dstPath; 117 while (pt = strchr(pt+1, '/')) { 118 *pt = '\0'; 119 if (isdir(a_dstPath)) { 120 if (mkdir(a_dstPath, 0755)) { 121 progerr(ERR_NODIR, a_dstPath, 122 errno, strerror(errno)); 123 *pt = '/'; 124 (void) close(srcFd); 125 return (-1); 126 } 127 } 128 *pt = '/'; 129 } 130 131 /* attempt to create target file again */ 132 dstFd = open(a_dstPath, O_WRONLY | O_TRUNC | O_CREAT, 133 srcStatbuf.st_mode); 134 if (dstFd < 0) { 135 progerr(ERR_OPEN_WRITE, a_dstPath, errno, 136 strerror(errno)); 137 (void) close(srcFd); 138 return (-1); 139 } 140 } 141 142 /* 143 * source and target files are open: copy data 144 */ 145 146 status = copyFile(srcFd, dstFd, a_srcPath, a_dstPath, &srcStatbuf, 0); 147 148 (void) close(srcFd); 149 (void) close(dstFd); 150 151 /* 152 * determine how to set access/modification times for target: 153 * -- a_mytime == 0: replicate source file access/modification times 154 * -- otherwise: use a_mytime for file access/modification times 155 */ 156 157 if (a_mytime == 0) { 158 times.actime = srcStatbuf.st_atime; 159 times.modtime = srcStatbuf.st_mtime; 160 } else { 161 times.actime = a_mytime; 162 times.modtime = a_mytime; 163 } 164 165 /* set access/modification times for target */ 166 167 if (utime(a_dstPath, ×) != 0) { 168 progerr(ERR_MODTIM, a_dstPath, errno, strerror(errno)); 169 return (-1); 170 } 171 172 /* return error if copy failed */ 173 174 if (status != 0) { 175 progerr(ERR_READ, a_srcPath, errno, strerror(errno)); 176 return (-1); 177 } 178 179 /* success! */ 180 181 return (0); 182 } 183 184 /* 185 * Name: copyFile 186 * Description: fast copy of file - use mmap()/write() loop if possible 187 * Arguments: int srcFd - file descriptor open on source file 188 * int dstFd - file descriptor open on target file 189 * char *srcPath - name of source file (for error messages) 190 * char *dstPath - name of target file (for error messages) 191 * struct stat *a_srcStatbuf - stat structure for source file 192 * long a_iosize - preferred io size for read/write loop 193 * Returns: int 194 * == 0 - successful 195 * != 0 - failure 196 */ 197 198 int 199 copyFile(int a_srcFd, int a_dstFd, char *a_srcPath, char *a_dstPath, 200 struct stat *a_srcStatbuf, long a_iosize) 201 { 202 caddr_t cp; 203 off_t filesize = a_srcStatbuf->st_size; 204 size_t mapsize = 0; 205 size_t munmapsize = 0; 206 off_t offset = 0; 207 208 echoDebug(DBG_COPY_FILE, a_srcPath, a_dstPath); 209 210 /* 211 * if the source is a regular file and is not "too small", then cause 212 * the file to be mapped into memory 213 */ 214 215 if (S_ISREG(a_srcStatbuf->st_mode) && (filesize > SMALLFILESIZE)) { 216 /* 217 * Determine size of initial mapping. This will determine the 218 * size of the address space chunk we work with. This initial 219 * mapping size will be used to perform munmap() in the future. 220 */ 221 222 mapsize = MAXMAPSIZE; 223 if (filesize < mapsize) { 224 mapsize = filesize; 225 } 226 227 /* 228 * remember size of mapping to "unmap" - if the source file 229 * exceeds MAXMAPSIZE bytes, then the final mapping of the 230 * source file will be less than MAXMAPSIZE, and we need to 231 * make sure that the entire mapping is unmapped when done. 232 */ 233 234 munmapsize = mapsize; 235 236 /* map the first segment of the source into memory */ 237 238 cp = mmap((caddr_t)NULL, mapsize, PROT_READ, 239 (MAP_SHARED|MAP_ALIGN), a_srcFd, (off_t)0); 240 if (cp == MAP_FAILED) { 241 mapsize = 0; /* can't mmap today */ 242 } 243 } 244 245 /* 246 * if the source was not mapped into memory, copy via read/write loop 247 */ 248 249 if (mapsize == 0) { 250 char *buf = (char *)NULL; 251 size_t blocksize; 252 int pagesize = getpagesize(); 253 254 /* set blocksize for copy */ 255 256 blocksize = a_iosize; 257 if ((blocksize == 0) || (blocksize > SMALLFILESIZE)) { 258 blocksize = SMALLFILESIZE; 259 } else if (blocksize < pagesize) { 260 blocksize = pagesize; 261 } 262 263 /* allocate i/o transfer buffer */ 264 265 buf = memalign((size_t)pagesize, blocksize); 266 if (buf == (char *)NULL) { 267 progerr(ERR_COPY_MEMORY, a_srcPath, errno, 268 strerror(errno)); 269 return (1); 270 } 271 272 /* copy the file contents */ 273 274 for (;;) { 275 ssize_t n; 276 277 /* read next block of data */ 278 279 n = read(a_srcFd, buf, blocksize); 280 if (n == 0) { 281 /* end of file - return success */ 282 (void) free(buf); 283 return (0); 284 } else if (n < 0) { 285 /* read error - return error */ 286 progerr(ERR_READ, a_srcPath, 287 errno, strerror(errno)); 288 (void) free(buf); 289 return (1); 290 } 291 292 /* write out block of data just read in */ 293 294 if (vfpSafeWrite(a_dstFd, buf, (size_t)n) != n) { 295 /* short write/write error - return error */ 296 progerr(ERR_WRITE, a_dstPath, 297 errno, strerror(errno)); 298 (void) free(buf); 299 return (1); 300 } 301 } 302 } 303 304 /* 305 * the source has been mapped into memory, copy via mappings 306 */ 307 308 for (;;) { 309 ssize_t nbytes; 310 311 /* write first mappings worth of data */ 312 313 nbytes = write(a_dstFd, cp, mapsize); 314 315 /* 316 * if we write less than the mmaped size it's due to a 317 * media error on the input file or out of space on 318 * the output file. So, try again, and look for errno. 319 */ 320 321 if ((nbytes >= 0) && (nbytes != (ssize_t)mapsize)) { 322 size_t remains; 323 324 remains = mapsize - nbytes; 325 while (remains > 0) { 326 nbytes = write(a_dstFd, 327 (cp + mapsize - remains), remains); 328 if (nbytes >= 0) { 329 remains -= nbytes; 330 if (remains == 0) { 331 nbytes = mapsize; 332 } 333 continue; 334 } 335 336 /* i/o error - report and exit */ 337 338 if (errno == ENOSPC) { 339 progerr(ERR_WRITE, a_dstPath, 340 errno, strerror(errno)); 341 } else { 342 progerr(ERR_READ, a_srcPath, 343 errno, strerror(errno)); 344 } 345 346 /* unmap source file mapping */ 347 (void) munmap(cp, munmapsize); 348 return (1); 349 } 350 } 351 352 /* 353 * although the write manual page doesn't specify this 354 * as a possible errno, it is set when the nfs read 355 * via the mmap'ed file is accessed, so report the 356 * problem as a source access problem, not a target file 357 * problem 358 */ 359 360 if (nbytes < 0) { 361 if (errno == EACCES) { 362 progerr(ERR_READ, a_srcPath, 363 errno, strerror(errno)); 364 } else { 365 progerr(ERR_WRITE, a_dstPath, 366 errno, strerror(errno)); 367 } 368 369 /* unmap source file mapping */ 370 (void) munmap(cp, munmapsize); 371 return (1); 372 } 373 374 filesize -= nbytes; 375 if (filesize == 0) { 376 break; 377 } 378 379 offset += nbytes; 380 if (filesize < mapsize) { 381 mapsize = filesize; 382 } 383 384 /* map next segment of file on top of existing mapping */ 385 386 cp = mmap(cp, mapsize, PROT_READ, (MAP_SHARED|MAP_FIXED), 387 a_srcFd, offset); 388 389 if (cp == MAP_FAILED) { 390 progerr(ERR_MAPFAILED, a_srcPath, errno, 391 strerror(errno)); 392 /* unmap source file mapping */ 393 (void) munmap(cp, munmapsize); 394 return (1); 395 } 396 } 397 398 /* unmap source file mapping */ 399 400 (void) munmap(cp, munmapsize); 401 402 return (0); 403 } 404 405 /* 406 * Name: openLocal 407 * Description: open a file and assure that the descriptor returned is open on 408 * a file that is local to the current system - if the file is not 409 * local to this system, copy the file to a temporary file first, 410 * and then pass a handle back opened on the temporary file 411 * Arguments: a_path - [RO, *RO] - (char *) 412 * Pointer to string representing the path to the file 413 * to open 414 * a_oflag - [RO, *RO] - (int) 415 * Integer representing the "mode" bits for an open(2) call 416 * a_tmpdir - [RO, *RO] - (char *) 417 * Pointer to string representing the path to a directory 418 * where a temporary copy of the file can be placed if 419 * the source file is not local to this system. If this is 420 * NULL or does not exist, P_tmpdir is used. 421 * Returns: int 422 * >= 0 - file descriptor opened on the file 423 * == -1 - failed to open - errno contains error code 424 * NOTE: If the file is not local and is copied locally, the file is 425 * setup in such a way that it will be removed when the last 426 * file descriptor opened on the file is closed - there is no need 427 * to know the path to the temporary file or to remove it 428 * when done. 429 */ 430 431 int 432 openLocal(char *a_path, int a_oflag, char *a_tmpdir) 433 { 434 char *bn; 435 char template[PATH_MAX]; 436 int fd; 437 int lerrno; 438 int n; 439 int tmpFd; 440 struct stat statbuf; 441 442 /* open source file */ 443 444 fd = open(a_path, a_oflag); 445 if (fd < 0) { 446 return (fd); 447 } 448 449 /* return open fd if the source file is not remote */ 450 451 if (!isFdRemote(fd)) { 452 return (fd); 453 } 454 455 /* 456 * source file is remote - must make a local copy 457 */ 458 459 /* get the source file's status */ 460 461 n = fstat(fd, &statbuf); 462 if (n < 0) { 463 lerrno = errno; 464 (void) close(fd); 465 errno = lerrno; 466 return (-1); 467 } 468 469 /* generate unique temporary file name */ 470 471 if ((a_tmpdir == (char *)NULL) || (*a_tmpdir == '\0') || 472 (isdir(a_tmpdir) != 0)) { 473 a_tmpdir = P_tmpdir; 474 } 475 bn = basename(a_path); 476 n = strlen(a_tmpdir); 477 n = snprintf(template, sizeof (template), "%s%s%sXXXXXX", 478 a_tmpdir, a_tmpdir[n-1] == '/' ? "" : "/", bn); 479 if (n > sizeof (template)) { 480 (void) close(fd); 481 return (EINVAL); 482 } 483 484 /* create the temporary file and open it */ 485 486 tmpFd = mkstemp(template); 487 if (tmpFd < 0) { 488 lerrno = errno; 489 (void) close(fd); 490 errno = lerrno; 491 return (tmpFd); 492 } 493 494 /* unlink the file so when it is closed it is automatically deleted */ 495 496 (void) unlink(template); 497 498 /* copy the source file to the temporary file */ 499 500 n = copyFile(fd, tmpFd, a_path, template, &statbuf, 0L); 501 lerrno = errno; 502 (void) close(fd); 503 if (n != 0) { 504 (void) close(tmpFd); 505 errno = lerrno; 506 return (-1); 507 } 508 509 /* return handle to temporary file created */ 510 511 return (tmpFd); 512 } 513