/* * Perform file copy from source to target */ static int copyfile(char *source, char *target) { int fi, fo; int mapsize, munmapsize; caddr_t cp; off_t filesize; off_t offset; int nbytes; int remains; int n; struct stat s1, s2; fi = open(source, O_RDONLY); if (fi == -1) { Perror(source); return (1); } DBG_MSG3("Copying file %s to %s", source, target); fo = open(target, O_WRONLY|O_CREAT|O_TRUNC); if (fo == -1) { Perror(target); (void) close(fi); return (1); } if (fstat(fi, &s1) < 0) { Perror(source); (void) close(fi); (void) close(fo); return (1); } if (fstat(fo, &s2) < 0) { Perror(source); (void) close(fi); (void) close(fo); return (1); } if (s1.st_size > SMALLFILESIZE) { /* * Determine size of initial mapping. This will determine the * size of the address space chunk we work with. This initial * mapping size will be used to perform munmap() in the future. */ mapsize = MAXMAPSIZE; if (s1.st_size < mapsize) mapsize = s1.st_size; munmapsize = mapsize; /* * Mmap time! */ if ((cp = mmap((caddr_t)NULL, mapsize, PROT_READ, MAP_SHARED, fi, (off_t)0)) == MAP_FAILED) mapsize = 0; /* can't mmap today */ } else mapsize = 0; filesize = s1.st_size; if (mapsize != 0) { offset = 0; for (;;) { nbytes = write(fo, cp, mapsize); /* * if we write less than the mmaped size it's due to a * media error on the input file or out of space on * the output file. So, try again, and look for errno. */ if ((nbytes >= 0) && (nbytes != (int)mapsize)) { remains = mapsize - nbytes; while (remains > 0) { nbytes = write(fo, cp + mapsize - remains, remains); if (nbytes < 0) { if (errno == ENOSPC) Perror(target); else Perror(source); (void) close(fi); (void) close(fo); (void) munmap(cp, munmapsize); (void) unlink(target); return (1); } remains -= nbytes; if (remains == 0) nbytes = mapsize; } } /* * although the write manual page doesn't specify this * as a possible errno, it is set when the nfs read * via the mmap'ed file is accessed, so report the * problem as a source access problem, not a target file * problem */ if (nbytes < 0) { if (errno == EACCES) Perror(source); else Perror(target); (void) close(fi); (void) close(fo); (void) munmap(cp, munmapsize); (void) unlink(target); return (1); } filesize -= nbytes; if (filesize == 0) break; offset += nbytes; if (filesize < mapsize) mapsize = filesize; if (mmap(cp, mapsize, PROT_READ, MAP_SHARED | MAP_FIXED, fi, offset) == MAP_FAILED) { Perror(source); (void) close(fi); (void) close(fo); (void) munmap(cp, munmapsize); (void) unlink(target); return (1); } } (void) munmap(cp, munmapsize); } else { char buf[SMALLFILESIZE]; for (;;) { n = read(fi, buf, sizeof (buf)); if (n == 0) { return (0); } else if (n < 0) { Perror(source); (void) close(fi); (void) close(fo); (void) unlink(target); return (1); } else if (write(fo, buf, n) != n) { Perror(target); (void) close(fi); (void) close(fo); (void) unlink(target); return (1); } } } return (0); }
/* * Main function for doing the copying of bits */ int TM_perform_transfer(nvlist_t *targs, void(*prog)(int)) { char *logfile = NULL, *buf = NULL, *cprefix; char *buf1 = NULL, *layout = NULL, *dbg; FILE *cpipe = NULL, *kbd_file; float ipercent, rem_percent, cpfiles; float calc_factor; int kbd = -1, kbd_layout; int rv = 0, i; struct file_list flist, *cflist; clock_t tm; struct stat st; char zerolist[PATH_MAX]; if (pthread_mutex_lock(&tran_mutex) != 0) { Perror("Unable to acquire Transfer lock "); return (1); } if (nvlist_lookup_string(targs, "mountpoint", &mntpt) != 0) { Perror("Alternate root mountpoint not provided. Bailing. "); return (1); } if (prog == NULL) { progress = log_progress; } else { progress = prog; } logfile = malloc(PATH_MAX); if (logfile == NULL) { Perror("Malloc failed "); return (1); } (void) snprintf(logfile, PATH_MAX, "%s/%s", mntpt, TM_LOGFILE_NAME); lof = fopen(logfile, "w+"); if (lof == NULL) { Perror("Unable to open logfile "); goto error_done; } buf = malloc(BUF_SIZE); if (buf == NULL) { Perror("Malloc failed "); goto error_done; } buf1 = malloc(BUF_SIZE); if (buf1 == NULL) { Perror("Malloc failed "); goto error_done; } dbg = getenv("TM_DEBUG"); if (dbg != NULL && strcmp(dbg, "1") == 0) { TM_enable_debug(); } /* * Set TMPDIR to avoid cpio depleting ramdisk space */ if (putenv(tmpenv) != 0) { Perror(tmpenv); goto error_done; } /* * Zero length file list. */ (void) strlcpy(zerolist, mntpt, PATH_MAX); (void) strlcat(zerolist, "/flist.0length", PATH_MAX); if ((zerolength = fopen(zerolist, "w+")) == NULL) { Perror(zerolist); goto error_done; } tm = time(NULL); (void) strftime(buf, PATH_MAX, (char *)0, localtime(&tm)); INFO_MSG2("-- Starting transfer process, %s --", buf); (void) chdir("/"); CHECK_ABORT; (*progress)(0); percent = 0; opercent = 0; total_find_percent = (NUM_PREFIXES - 1) * FIND_PERCENT; /* * Get the optimized libc overlay out of the way. */ if (umount("/lib/libc.so.1") != 0) { if (errno != EINVAL) { Perror("Can't unmount /lib/libc.so.1 "); goto error_done; } } CHECK_ABORT; INFO_MSG1("Building file lists for cpio"); /* * Do a file tree walk of all the mountpoints provided and * build up pathname lists. Pathname lists of all mountpoints * under the same prefix are aggregated in the same file to * reduce the number of cpio invocations. * * This loop builds a linked list where each entry points to * a file containing a pathname list and mentions other info * like the mountpoint from which to copy etc. */ cprefix = ""; flist.next = NULL; cflist = &flist; for (i = 0; cpio_prefixes[i].chdir_prefix != NULL; i++) { char *patt; regex_t re; CHECK_ABORT; DBG_MSG3("Cpio dir: %s, Chdir to: %s", cpio_prefixes[i].cpio_dir, cpio_prefixes[i].chdir_prefix); patt = cpio_prefixes[i].match_pattern; if (strcmp(cprefix, cpio_prefixes[i].chdir_prefix) != 0 || patt != NULL || cpio_prefixes[i].clobber_files == 1 || cpio_prefixes[i].cpio_args != NULL) { cprefix = cpio_prefixes[i].chdir_prefix; cflist->next = (struct file_list *) malloc(sizeof (struct file_list)); cflist = cflist->next; cflist->next = NULL; (void) snprintf(cflist->name, PATH_MAX, "%s/flist%d", mntpt, i); DBG_MSG2(" File list tempfile: %s", cflist->name); cflist->handle = fopen(cflist->name, "w+"); if (cflist->handle == NULL) { Perror("Unable to open file list "); goto error_done; } cflist->chdir_prefix = cpio_prefixes[i].chdir_prefix; if (patt != NULL) { DBG_MSG2(" Compiling regexp: %s", patt); if (patt[0] == '!') { negate = 1; patt++; } else { negate = 0; } if (regcomp(&re, patt, REG_EXTENDED|REG_NOSUB) != 0) { Perror("Regexp error "); goto error_done; } mre = &re; } else { mre = NULL; } listfile = cflist->handle; cflist->clobber_files = cpio_prefixes[i].clobber_files; if (cpio_prefixes[i].cpio_args != NULL) { cflist->cpio_args = cpio_prefixes[i].cpio_args; } else { cflist->cpio_args = DEFAULT_CPIO_ARGS; } } INFO_MSG3("Scanning %s/%s", cflist->chdir_prefix, cpio_prefixes[i].cpio_dir); (void) chdir(cflist->chdir_prefix); if (nftw(cpio_prefixes[i].cpio_dir, add_files, 10, FTW_MOUNT|FTW_PHYS) < 0) { Perror("Nftw failed "); goto error_done; } (void) fflush(cflist->handle); } (void) fflush(zerolength); /* * Now process each entry in the list. cpio is executed with the * -V option where it prints a dot for each pathname processed. * Since we already know the number of files we can show accurate * percentage completion. */ INFO_MSG1("Beginning cpio actions ..."); rem_percent = 95 - percent; ipercent = percent; cflist = flist.next; cpfiles = 0; opercent = 0; percent = 0; calc_factor = rem_percent / nfiles; while (cflist != NULL) { (void) fclose(cflist->handle); cflist->handle = NULL; CHECK_ABORT; if (cflist->clobber_files) { if (do_clobber_files(cflist->name) != 0) { goto error_done; } } (void) chdir(cflist->chdir_prefix); (void) snprintf(buf, PATH_MAX, "%s -%sV %s < %s", CPIO, cflist->cpio_args, mntpt, cflist->name); DBG_MSG3("Executing: %s, CWD: %s", buf, cflist->chdir_prefix); cpipe = popen(buf, "r"); if (cpipe == NULL) { Perror("Unable to cpio files "); goto error_done; } while (!feof(cpipe)) { int ch = fgetc(cpipe); if (ch == '.') { cpfiles++; percent = (int)(cpfiles * calc_factor + ipercent); if (percent - opercent >= 1) { if (progress != NULL) { (*progress)(percent); } opercent = percent; } } CHECK_ABORT; } if (ferror(cpipe)) { Perror(CPIO); goto error_done; } (void) fclose(cpipe); cpipe = NULL; (void) unlink(cflist->name); cflist->name[0] = '\0'; cflist = cflist->next; } (*progress)(percent); cpipe = NULL; /* * Process zero-length files if any. */ INFO_MSG1("Creating zero-length files"); rewind(zerolength); while (fgets(buf, BUF_SIZE, zerolength) != NULL) { int fd; mode_t mod; uid_t st_uid, st_gid; char *token, *lasts; /* Get the newline out of the way */ buf[strlen(buf) - 1] = '\0'; /* Parse out ownership and perms */ GET_TOKEN(token, lasts, buf, ","); mod = atoi(token); GET_TOKEN(token, lasts, NULL, ","); st_uid = atoi(token); GET_TOKEN(token, lasts, NULL, ","); st_gid = atoi(token); GET_TOKEN(token, lasts, NULL, ","); (void) snprintf(buf1, PATH_MAX, "%s/%s", mntpt, token); fd = open(buf1, O_WRONLY | O_CREAT | O_TRUNC, mod); if (fd != -1) { (void) fchown(fd, st_uid, st_gid); (void) close(fd); DBG_MSG2("Created file %s", buf1); } else { INFO_MSG1("Unable to create file:"); Perror(buf1); } } (*progress)(97); CHECK_ABORT; INFO_MSG1("Extracting archive"); (void) chdir(mntpt); (void) snprintf(buf, PATH_MAX, "%s e -so %s | %s -idum", SZIP, ARCHIVE, CPIO); DBG_MSG3("Executing: %s, CWD: %s", buf, mntpt); if (system(buf) != 0) { Perror("Extracting archive failed "); goto error_done; } (*progress)(98); CHECK_ABORT; /* * Check for the presence of skeleton.cpio before extracting it. * This file may not be present in a Distro Constructor image. */ if (lstat(SKELETON, &st) == 0 && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) { INFO_MSG1("Extracting skeleton archive"); (void) snprintf(buf, PATH_MAX, "%s -imu < %s", CPIO, SKELETON, mntpt); DBG_MSG3("Executing: %s, CWD: %s", buf, mntpt); if (system(buf) != 0) { Perror("Skeleton cpio failed "); goto error_done; } } (*progress)(99); CHECK_ABORT; INFO_MSG1("Performing file operations"); for (i = 0; i < NUM_FILEOPS_LIST; i++) { int rv; CHECK_ABORT; expand_symbols(fileops_list[i].src, buf, PATH_MAX); switch (fileops_list[i].op) { int op; case FILE_OP_UNLINK: DBG_MSG2("Unlink: %s", buf); (void) unlink(buf); rv = 0; /* unlink errors are non-fatal */ break; case FILE_OP_RMDIR: DBG_MSG2("Rmdir: %s", buf); (void) rmdir(buf); rv = 0; /* Ignore rmdir errors for now */ break; case FILE_OP_MKDIR: DBG_MSG2("Mkdir: %s", buf); rv = 0; if (lstat(buf, &st) == 0) { op = 0; if ((st.st_mode & S_IFMT) != S_IFDIR) { rv = unlink(buf); op = 1; } if (rv == 0 && op) { rv = mkdir(buf, fileops_list[i].perms); } } else { rv = mkdir(buf, fileops_list[i].perms); } break; case FILE_OP_COPY: expand_symbols(fileops_list[i].dst, buf1, PATH_MAX); rv = copyfile(buf, buf1); break; case FILE_OP_CHMOD: expand_symbols(fileops_list[i].dst, buf1, PATH_MAX); rv = chmod(buf, fileops_list[i].perms); break; default: Perror("Unsupported file operation "); rv = 1; break; } if (rv != 0) { Perror("File ops error "); Perror(buf); goto error_done; } } CHECK_ABORT; INFO_MSG1("Fetching and updating keyboard layout"); (void) chdir(mntpt); DBG_MSG2("Opening keyboard device: %s", KBD_DEVICE); kbd = open(KBD_DEVICE, O_RDWR); if (kbd < 0) { Perror("Error opening keyboard"); goto error_done; } if (ioctl(kbd, KIOCLAYOUT, &kbd_layout)) { Perror("ioctl keyboard layout"); goto error_done; } CHECK_ABORT; if ((layout = get_layout_name(kbd_layout)) == NULL) { goto error_done; } kbd_file = fopen(KBD_DEFAULTS_FILE, "a+"); if (kbd_file == NULL) { Perror("Unable to open kbd defaults file "); goto error_done; } (void) fprintf(kbd_file, "LAYOUT=%s\n", layout); (void) fclose(kbd_file); DBG_MSG3("Updated keyboard defaults file: %s/%s", mntpt, KBD_DEFAULTS_FILE); INFO_MSG2("Detected %s keyboard layout", layout); tm = time(NULL); (void) strftime(buf, PATH_MAX, (char *)0, localtime(&tm)); INFO_MSG2("-- Completed transfer process, %s --", buf); (*progress)(100); goto done; error_done: rv = 1; done: if (lof != NULL) (void) fclose(lof); if (cpipe != NULL) (void) fclose(cpipe); free_flist(flist.next); if (logfile != NULL) free(logfile); if (kbd > 0) (void) close(kbd); if (buf != NULL) free(buf); if (buf1 != NULL) free(buf1); if (layout != NULL) free(layout); if (zerolength != NULL) { (void) fclose(zerolength); (void) unlink(zerolist); } do_abort = 0; (void) pthread_mutex_unlock(&tran_mutex); return (rv); }
/* Main routine for the server threads */ thr_startfunc_t serve_pipe(void *data) { char sio_buf[BUFSIZ], sock_buf[BUFSIZ]; int fd_max, sio_fd, sock_fd; int sio_count, sock_count; int res, port; fd_set rfds, wfds; pipe_s *pipe = (pipe_s *)data; #if defined(__UNIX__) struct timeval tv = {pipe->timeout, 0}; struct timeval *ptv = &tv; #elif defined(__WIN32__) struct timeval tv = {0,10000}; struct timeval *ptv = &tv; DWORD msecs = 0, timeout = pipe->timeout * 1000; #endif port = pipe->sio.info.port; /* Only proceed if we can lock the mutex */ if (thr_mutex_trylock(pipe->mutex)) { error("server(%d) - resource is locked", port); } else { sio_count = 0; sock_count = 0; sio_fd = pipe->sio.fd; sock_fd = pipe->sock.fd; #if defined(__UNIX__) fd_max = sio_fd > sock_fd ? sio_fd : sock_fd; #elif defined(__WIN32__) fd_max = sock_fd; msecs = GetTickCount(); #endif fprintf(stderr, "server(%d) - thread started\n", port); while (1) { FD_ZERO(&rfds); FD_ZERO(&wfds); #if defined(__UNIX__) /* Always ask for read notification to check for EOF */ FD_SET(sio_fd, &rfds); /* Only ask for write notification if we have something to write */ if (sock_count > 0) FD_SET(sio_fd, &wfds); /* Reset timeout values */ tv.tv_sec = pipe->timeout; tv.tv_usec = 0; #endif /* Always ask for read notification to check for EOF */ FD_SET(sock_fd, &rfds); /* Only ask for write notification if we have something to write */ if (sio_count > 0) FD_SET(sock_fd, &wfds); //DBG_MSG2("server(%d) waiting for events", port); /* Wait for read/write events */ res = select(fd_max + 1, &rfds, &wfds, NULL, ptv); if (res == -1) { perror2("server(%d) - select()", port); break; } #if defined(__UNIX__) /* Use the select result for timeout detection */ if (res == 0) { fprintf(stderr, "server(%d) - timed out\n", port); break; } /* Input from serial port? */ if (FD_ISSET(sio_fd, &rfds)) #elif defined(__WIN32__) if (1) #endif { /* Only read input if buffer is empty */ if (sio_count == 0) { sio_count = sio_read(&pipe->sio, sio_buf, sizeof(sio_buf)); if (sio_count <= 0) { if (sio_count == 0) { #if defined(__UNIX__) fprintf(stderr, "server(%d) - EOF from sio\n", port); break; #endif } else { perror2("server(%d) - read(sio)", port); break; } } else { DBG_MSG3("server(%d) - read %d bytes from sio", port, sio_count); } } } /* Write to socket possible? */ if (FD_ISSET(sock_fd, &wfds)) { if (sio_count > 0) { if ((res = tcp_write(&pipe->sock, sio_buf, sio_count)) < 0) { perror2("server(%d) - write(sock)", port); break; } DBG_MSG3("server(%d) - Wrote %d bytes to sock", port, res); sio_count -= res; } } /* Input from socket? */ if (FD_ISSET(sock_fd, &rfds)) { /* Only read input if buffer is empty */ if (sock_count == 0) { sock_count = tcp_read(&pipe->sock, sock_buf, sizeof(sock_buf)); if (sock_count <= 0) { if (sock_count == 0) { fprintf(stderr, "server(%d) - EOF from sock\n", port); break; } else { perror2("server(%d) - read(sock)", port); break; } } DBG_MSG3("server(%d) - read %d bytes from sock", port, sock_count); } } #if defined(__UNIX__) /* Write to serial port possible? */ if (FD_ISSET(sio_fd, &wfds)) #elif defined(__WIN32__) /* No socket IO performed? */ if ((!FD_ISSET(sock_fd, &rfds)) && (!FD_ISSET(sock_fd, &wfds))) { /* Break on a time out */ if (GetTickCount() - msecs > timeout) { fprintf(stderr, "server(%d) - timed out\n", port); break; } } else { msecs = GetTickCount(); } if (1) #endif { if (sock_count > 0) { if ((res = sio_write(&pipe->sio, sock_buf, sock_count)) < 0) { perror2("server(%d) - write(sio)", port); break; } DBG_MSG3("server(%d) - wrote %d bytes to sio", port, res); sock_count -= res; } } } /* Unlock our mutex */ thr_mutex_unlock(pipe->mutex); } fprintf(stderr, "server(%d) exiting\n", port); /* Clean up - don't call pipe_cleanup() as that would nuke our mutex */ sio_cleanup(&pipe->sio); tcp_cleanup(&pipe->sock); free(pipe); thr_exit((thr_exitcode_t)0); return (thr_exitcode_t)0; }