int cdb_make_put(struct cdb_make *cdbmp, const void *key, unsigned klen, const void *val, unsigned vlen, enum cdb_put_mode mode) { unsigned hval = cdb_hash(key, klen); int r; switch(mode) { case CDB_PUT_REPLACE: case CDB_PUT_INSERT: case CDB_PUT_WARN: case CDB_PUT_REPLACE0: r = findrec(cdbmp, key, klen, hval, mode); if (r < 0) return -1; if (r && mode == CDB_PUT_INSERT) return errno = EEXIST, 1; break; case CDB_PUT_ADD: r = 0; break; default: return errno = EINVAL, -1; } if (_cdb_make_add(cdbmp, hval, key, klen, val, vlen) < 0) return -1; return r; }
int cdb_make_find(struct cdb_make *cdbmp, const void *key, unsigned klen, enum cdb_put_mode mode) { return findrec(cdbmp, key, klen, cdb_hash(key, klen), mode); }
/* * Extract a file from the archive. */ void extract_archive () { register char *data; int fd, check, namelen, written, openflag; long size; struct utimbuf acc_upd_times; register int skipcrud; register int i; /* int sparse_ind = 0;*/ union record *exhdr; struct saved_dir_info *tmp; /* int end_nulls; */ saverec (&head); /* Make sure it sticks around */ userec (head); /* And go past it in the archive */ decode_header (head, &hstat, &head_standard, 1); /* Snarf fields */ if ((f_confirm && !confirm ("extract", current_file_name)) || (f_exstdout && head->header.linkflag != LF_OLDNORMAL && head->header.linkflag != LF_NORMAL && head->header.linkflag != LF_CONTIG)) { if (head->header.isextended) skip_extended_headers (); skip_file ((long) hstat.st_size); saverec ((union record **) 0); return; } /* Print the record from 'head' and 'hstat' */ if (f_verbose) print_header (); /* * Check for fully specified pathnames and other atrocities. * * Note, we can't just make a pointer to the new file name, * since saverec() might move the header and adjust "head". * We have to start from "head" every time we want to touch * the header record. */ skipcrud = 0; while (!f_absolute_paths && '/' == current_file_name[skipcrud]) { static int warned_once = 0; skipcrud++; /* Force relative path */ if (!warned_once++) { msg ("Removing leading / from absolute path names in the archive."); } } switch (head->header.linkflag) { default: msg ("Unknown file type '%c' for %s, extracted as normal file", head->header.linkflag, skipcrud + current_file_name); /* FALL THRU */ /* * JK - What we want to do if the file is sparse is loop through * the array of sparse structures in the header and read in * and translate the character strings representing 1) the offset * at which to write and 2) how many bytes to write into numbers, * which we store into the scratch array, "sparsearray". This * array makes our life easier the same way it did in creating * the tar file that had to deal with a sparse file. * * After we read in the first five (at most) sparse structures, * we check to see if the file has an extended header, i.e., * if more sparse structures are needed to describe the contents * of the new file. If so, we read in the extended headers * and continue to store their contents into the sparsearray. */ case LF_SPARSE: sp_array_size = 10; sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array)); for (i = 0; i < SPARSE_IN_HDR; i++) { sparsearray[i].offset = from_oct (1 + 12, head->header.sp[i].offset); sparsearray[i].numbytes = from_oct (1 + 12, head->header.sp[i].numbytes); if (!sparsearray[i].numbytes) break; } /* end_nulls = from_oct(1+12, head->header.ending_blanks);*/ if (head->header.isextended) { /* read in the list of extended headers and translate them into the sparsearray as before */ /* static */ int ind = SPARSE_IN_HDR; for (;;) { exhdr = findrec (); for (i = 0; i < SPARSE_EXT_HDR; i++) { if (i + ind > sp_array_size - 1) { /* * realloc the scratch area * since we've run out of room -- */ sparsearray = (struct sp_array *) ck_realloc (sparsearray, 2 * sp_array_size * (sizeof (struct sp_array))); sp_array_size *= 2; } if (!exhdr->ext_hdr.sp[i].numbytes) break; sparsearray[i + ind].offset = from_oct (1 + 12, exhdr->ext_hdr.sp[i].offset); sparsearray[i + ind].numbytes = from_oct (1 + 12, exhdr->ext_hdr.sp[i].numbytes); } if (!exhdr->ext_hdr.isextended) break; else { ind += SPARSE_EXT_HDR; userec (exhdr); } } userec (exhdr); } /* FALL THRU */ case LF_OLDNORMAL: case LF_NORMAL: case LF_CONTIG: /* * Appears to be a file. * See if it's really a directory. */ namelen = strlen (skipcrud + current_file_name) - 1; if (current_file_name[skipcrud + namelen] == '/') goto really_dir; /* FIXME, deal with protection issues */ again_file: openflag = (f_keep ? O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL : O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC) | ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND); /* * JK - The last | is a kludge to solve the problem * the O_APPEND flag causes with files we are * trying to make sparse: when a file is opened * with O_APPEND, it writes to the last place * that something was written, thereby ignoring * any lseeks that we have done. We add this * extra condition to make it able to lseek when * a file is sparse, i.e., we don't open the new * file with this flag. (Grump -- this bug caused * me to waste a good deal of time, I might add) */ if (f_exstdout) { fd = 1; goto extract_file; } if (f_unlink && !f_keep) { if (unlink(skipcrud + current_file_name) == -1) if (errno != ENOENT) msg_perror ("Could not unlink %s", skipcrud + current_file_name); } #ifdef O_CTG /* * Contiguous files (on the Masscomp) have to specify * the size in the open call that creates them. */ if (head->header.linkflag == LF_CONTIG) fd = open ((longname ? longname : head->header.name) + skipcrud, openflag | O_CTG, hstat.st_mode, hstat.st_size); else #endif { #ifdef NO_OPEN3 /* * On raw V7 we won't let them specify -k (f_keep), but * we just bull ahead and create the files. */ fd = creat ((longname ? longname : head->header.name) + skipcrud, hstat.st_mode); #else /* * With 3-arg open(), we can do this up right. */ fd = open (skipcrud + current_file_name, openflag, hstat.st_mode); #endif } if (fd < 0) { if (make_dirs (skipcrud + current_file_name)) goto again_file; msg_perror ("Could not create file %s", skipcrud + current_file_name); if (head->header.isextended) skip_extended_headers (); skip_file ((long) hstat.st_size); goto quit; } extract_file: if (head->header.linkflag == LF_SPARSE) { char *name; int namelen; /* * Kludge alert. NAME is assigned to header.name * because during the extraction, the space that * contains the header will get scribbled on, and * the name will get munged, so any error messages * that happen to contain the filename will look * REAL interesting unless we do this. */ namelen = strlen (skipcrud + current_file_name) + 1; name = (char *) ck_malloc ((sizeof (char)) * namelen); bcopy (skipcrud + current_file_name, name, namelen); size = hstat.st_size; extract_sparse_file (fd, &size, hstat.st_size, name); } else for (size = hstat.st_size; size > 0; size -= written) { /* long offset, numbytes;*/ if (f_multivol) { save_name = current_file_name; save_totsize = hstat.st_size; save_sizeleft = size; } /* * Locate data, determine max length * writeable, write it, record that * we have used the data, then check * if the write worked. */ data = findrec ()->charptr; if (data == NULL) { /* Check it... */ msg ("Unexpected EOF on archive file"); break; } /* * JK - If the file is sparse, use the sparsearray * that we created before to lseek into the new * file the proper amount, and to see how many * bytes we want to write at that position. */ /* if (head->header.linkflag == LF_SPARSE) { off_t pos; pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0); printf("%d at %d\n", (int) pos, sparse_ind); written = sparsearray[sparse_ind++].numbytes; } else*/ written = endofrecs ()->charptr - data; if (written > size) written = size; errno = 0; check = write (fd, data, written); /* * The following is in violation of strict * typing, since the arg to userec * should be a struct rec *. FIXME. */ userec ((union record *) (data + written - 1)); if (check == written) continue; /* * Error in writing to file. * Print it, skip to next file in archive. */ if (check < 0) msg_perror ("couldn't write to file %s", skipcrud + current_file_name); else msg ("could only write %d of %d bytes to file %s", check, written, skipcrud + current_file_name); skip_file ((long) (size - written)); break; /* Still do the close, mod time, chmod, etc */ } if (f_multivol) save_name = 0; /* If writing to stdout, don't try to do anything to the filename; it doesn't exist, or we don't want to touch it anyway */ if (f_exstdout) break; /* if (head->header.isextended) { register union record *exhdr; register int i; for (i = 0; i < 21; i++) { long offset; if (!exhdr->ext_hdr.sp[i].numbytes) break; offset = from_oct(1+12, exhdr->ext_hdr.sp[i].offset); written = from_oct(1+12, exhdr->ext_hdr.sp[i].numbytes); lseek(fd, offset, 0); check = write(fd, data, written); if (check == written) continue; } }*/ check = close (fd); if (check < 0) { msg_perror ("Error while closing %s", skipcrud + current_file_name); } set_filestat: /* * If we are root, set the owner and group of the extracted * file. This does what is wanted both on real Unix and on * System V. If we are running as a user, we extract as that * user; if running as root, we extract as the original owner. */ if (we_are_root || f_do_chown) { if (chown (skipcrud + current_file_name, hstat.st_uid, hstat.st_gid) < 0) { msg_perror ("cannot chown file %s to uid %d gid %d", skipcrud + current_file_name, hstat.st_uid, hstat.st_gid); } } /* * Set the modified time of the file. * * Note that we set the accessed time to "now", which * is really "the time we started extracting files". * unless f_gnudump is used, in which case .st_atime is used */ if (!f_modified) { /* fixme if f_gnudump should set ctime too, but how? */ if (f_gnudump) acc_upd_times.actime = hstat.st_atime; else acc_upd_times.actime = now; /* Accessed now */ acc_upd_times.modtime = hstat.st_mtime; /* Mod'd */ if (utime (skipcrud + current_file_name, &acc_upd_times) < 0) { msg_perror ("couldn't change access and modification times of %s", skipcrud + current_file_name); } } /* We do the utime before the chmod because some versions of utime are broken and trash the modes of the file. Since we then change the mode anyway, we don't care. . . */ /* * If '-k' is not set, open() or creat() could have saved * the permission bits from a previously created file, * ignoring the ones we specified. * Even if -k is set, if the file has abnormal * mode bits, we must chmod since writing or chown() has * probably reset them. * * If -k is set, we know *we* created this file, so the mode * bits were set by our open(). If the file is "normal", we * skip the chmod. This works because we did umask(0) if -p * is set, so umask will have left the specified mode alone. */ if ((!f_keep) || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX))) { if (chmod (skipcrud + current_file_name, notumask & (int) hstat.st_mode) < 0) { msg_perror ("cannot change mode of file %s to 0%o", skipcrud + current_file_name, notumask & (int) hstat.st_mode); } } quit: break; case LF_LINK: again_link: { struct stat st1, st2; if (f_unlink && !f_keep) { if (unlink(skipcrud + current_file_name) == -1) if (errno != ENOENT) msg_perror ("Could not unlink %s", skipcrud + current_file_name); } check = link (current_link_name, skipcrud + current_file_name); if (check == 0) break; if (make_dirs (skipcrud + current_file_name)) goto again_link; if (f_gnudump && errno == EEXIST) break; if (stat (current_link_name, &st1) == 0 && stat (current_file_name + skipcrud, &st2) == 0 && st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) break; msg_perror ("Could not link %s to %s", skipcrud + current_file_name, current_link_name); } break; #ifdef S_ISLNK case LF_SYMLINK: again_symlink: if (f_unlink && !f_keep) { if (unlink(skipcrud + current_file_name) == -1) if (errno != ENOENT) msg_perror ("Could not unlink %s", skipcrud + current_file_name); } check = symlink (current_link_name, skipcrud + current_file_name); /* FIXME, don't worry uid, gid, etc... */ if (check == 0) break; if (make_dirs (current_file_name + skipcrud)) goto again_symlink; msg_perror ("Could not create symlink to %s", current_link_name); break; #endif #ifdef S_IFCHR case LF_CHR: hstat.st_mode |= S_IFCHR; goto make_node; #endif #ifdef S_IFBLK case LF_BLK: hstat.st_mode |= S_IFBLK; #endif #if defined(S_IFCHR) || defined(S_IFBLK) make_node: if (f_unlink && !f_keep) { if (unlink(skipcrud + current_file_name) == -1) if (errno != ENOENT) msg_perror ("Could not unlink %s", skipcrud + current_file_name); } check = mknod (current_file_name + skipcrud, (int) hstat.st_mode, (int) hstat.st_rdev); if (check != 0) { if (make_dirs (skipcrud + current_file_name)) goto make_node; msg_perror ("Could not make %s", current_file_name + skipcrud); break; }; goto set_filestat; #endif #ifdef S_ISFIFO /* If local system doesn't support FIFOs, use default case */ case LF_FIFO: make_fifo: if (f_unlink && !f_keep) { if (unlink(skipcrud + current_file_name) == -1) if (errno != ENOENT) msg_perror ("Could not unlink %s", skipcrud + current_file_name); } check = mkfifo (current_file_name + skipcrud, (int) hstat.st_mode); if (check != 0) { if (make_dirs (current_file_name + skipcrud)) goto make_fifo; msg_perror ("Could not make %s", skipcrud + current_file_name); break; }; goto set_filestat; #endif case LF_DIR: case LF_DUMPDIR: namelen = strlen (current_file_name + skipcrud) - 1; really_dir: /* Check for trailing /, and zap as many as we find. */ while (namelen && current_file_name[skipcrud + namelen] == '/') current_file_name[skipcrud + namelen--] = '\0'; if (f_gnudump) { /* Read the entry and delete files that aren't listed in the archive */ gnu_restore (skipcrud); } else if (head->header.linkflag == LF_DUMPDIR) skip_file ((long) (hstat.st_size)); again_dir: check = mkdir (skipcrud + current_file_name, (we_are_root ? 0 : 0300) | (int) hstat.st_mode); if (check != 0) { struct stat st1; if (make_dirs (skipcrud + current_file_name)) goto again_dir; /* If we're trying to create '.', let it be. */ if (current_file_name[skipcrud + namelen] == '.' && (namelen == 0 || current_file_name[skipcrud + namelen - 1] == '/')) goto check_perms; if (errno == EEXIST && stat (skipcrud + current_file_name, &st1) == 0 && (S_ISDIR (st1.st_mode))) break; msg_perror ("Could not create directory %s", skipcrud + current_file_name); break; } check_perms: if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode)) { hstat.st_mode |= 0300; msg ("Added write and execute permission to directory %s", skipcrud + current_file_name); } /* * If we are root, set the owner and group of the extracted * file. This does what is wanted both on real Unix and on * System V. If we are running as a user, we extract as that * user; if running as root, we extract as the original owner. */ if (we_are_root || f_do_chown) { if (chown (skipcrud + current_file_name, hstat.st_uid, hstat.st_gid) < 0) { msg_perror ("cannot chown file %s to uid %d gid %d", skipcrud + current_file_name, hstat.st_uid, hstat.st_gid); } } if (!f_modified) { tmp = ((struct saved_dir_info *) ck_malloc (sizeof (struct saved_dir_info))); tmp->path = (char *) ck_malloc (strlen (skipcrud + current_file_name) + 1); strcpy (tmp->path, skipcrud + current_file_name); tmp->mode = hstat.st_mode; tmp->atime = hstat.st_atime; tmp->mtime = hstat.st_mtime; tmp->next = saved_dir_info_head; saved_dir_info_head = tmp; } else { /* This functions exactly as the code for set_filestat above. */ if ((!f_keep) || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX))) { if (chmod (skipcrud + current_file_name, notumask & (int) hstat.st_mode) < 0) { msg_perror ("cannot change mode of file %s to 0%o", skipcrud + current_file_name, notumask & (int) hstat.st_mode); } } } break; case LF_VOLHDR: if (f_verbose) { printf ("Reading %s\n", current_file_name); } break; case LF_NAMES: extract_mangle (head); break; case LF_MULTIVOL: msg ("Can't extract '%s'--file is continued from another volume\n", current_file_name); skip_file ((long) hstat.st_size); break; case LF_LONGNAME: case LF_LONGLINK: msg ("Visible long name error\n"); skip_file ((long) hstat.st_size); break; } /* We don't need to save it any longer. */ saverec ((union record **) 0);/* Unsave it */ }
nkconf_t *get_nkconfig(char *key, int flags, char **resultkey) { static RbtHandle handle; static char *realkey = NULL; void *k1, *k2; nkconf_t *result = NULL; int isclone; if (resultkey) *resultkey = NULL; switch (flags) { case NKCONF_TIMEFILTER: handle = findrec(key); /* We may have hit a cloned record, so use the real key for further searches */ if (handle != rbtEnd(rbconf)) { rbtKeyValue(rbconf, handle, &k1, &k2); realkey = k1; } while (handle != rbtEnd(rbconf)) { rbtKeyValue(rbconf, handle, &k1, &k2); result = (nkconf_t *)k2; if (timecheck(result->starttime, result->endtime, result->nktime)) return result; /* Go to the next */ handle = rbtNext(rbconf, handle); if (handle != rbtEnd(rbconf)) { rbtKeyValue(rbconf, handle, &k1, &k2); if (strncmp(realkey, ((nkconf_t *)k2)->key, strlen(realkey)) != 0) handle=rbtEnd(rbconf); } } realkey = NULL; break; case NKCONF_FIRSTMATCH: handle = findrec(key); realkey = NULL; if (handle != rbtEnd(rbconf)) { rbtKeyValue(rbconf, handle, &k1, &k2); realkey = (char *)k1; } break; case NKCONF_FIRST: realkey = NULL; handle = rbtBegin(rbconf); if (handle == rbtEnd(rbconf)) return NULL; do { rbtKeyValue(rbconf, handle, &k1, &k2); realkey = (char *)k1; isclone = (*(realkey + strlen(realkey) - 1) == '='); if (isclone) handle = rbtNext(rbconf, handle); } while (isclone && (handle != rbtEnd(rbconf))); break; case NKCONF_NEXT: if (!realkey || (handle == rbtEnd(rbconf))) return NULL; isclone = 1; while (isclone && (handle != rbtEnd(rbconf))) { handle = rbtNext(rbconf, handle); if (handle) { rbtKeyValue(rbconf, handle, &k1, &k2); realkey = (char *)k1; isclone = (*(realkey + strlen(realkey) - 1) == '='); } } break; case NKCONF_RAW_FIRST: handle = rbtBegin(rbconf); realkey = NULL; break; case NKCONF_RAW_NEXT: handle = rbtNext(rbconf, handle); realkey = NULL; break; case NKCONF_FIRSTHOSTMATCH: do { int found = 0; char *delim; realkey = NULL; handle = rbtBegin(rbconf); while (!found && (handle != rbtEnd(rbconf))) { rbtKeyValue(rbconf, handle, &k1, &k2); realkey = (char *)k1; delim = realkey + strlen(key); /* OK even if past end of realkey */ found = ((strncmp(realkey, key, strlen(key)) == 0) && ((*delim == '|') || (*delim == '='))); if (!found) { handle = rbtNext(rbconf, handle); realkey = NULL; } } if ((handle != rbtEnd(rbconf)) && (*(realkey + strlen(realkey) - 1) == '=')) { key = (char *)k2; isclone = 1; } else isclone = 0; } while (isclone && (handle != rbtEnd(rbconf))); break; } if (handle == rbtEnd(rbconf)) { realkey = NULL; return NULL; } rbtKeyValue(rbconf, handle, &k1, &k2); if (resultkey) *resultkey = (char *)k1; result = (nkconf_t *)k2; return result; }
static void fill_in_sparse_array (void) { int ind; /* Allocate space for our scratch space; it's initially 10 elements long, but can change in this routine if necessary. */ sp_array_size = 10; sparsearray = (struct sp_array *) tar_xmalloc (sp_array_size * sizeof (struct sp_array)); /* There are at most five of these structures in the header itself; read these in first. */ for (ind = 0; ind < SPARSE_IN_HDR; ind++) { /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler. */ if (head->header.sp[ind].numbytes == 0) break; sparsearray[ind].offset = from_oct (1 + 12, head->header.sp[ind].offset); sparsearray[ind].numbytes = from_oct (1 + 12, head->header.sp[ind].numbytes); } /* If the header's extended, we gotta read in exhdr's till we're done. */ if (head->header.isextended) { /* How far into the sparsearray we are `so far'. */ static int so_far_ind = SPARSE_IN_HDR; union record *exhdr; while (1) { exhdr = findrec (); for (ind = 0; ind < SPARSE_EXT_HDR; ind++) { if (ind + so_far_ind > sp_array_size - 1) { /* We just ran out of room in our scratch area - realloc it. */ sp_array_size *= 2; sparsearray = (struct sp_array *) tar_realloc (sparsearray, sp_array_size * sizeof (struct sp_array)); } /* Convert the character strings into longs. */ sparsearray[ind + so_far_ind].offset = from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset); sparsearray[ind + so_far_ind].numbytes = from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes); } /* If this is the last extended header for this file, we can stop. */ if (!exhdr->ext_hdr.isextended) break; else { so_far_ind += SPARSE_EXT_HDR; userec (exhdr); } } /* Be sure to skip past the last one. */ userec (exhdr); } }
static void diff_sparse_files (int filesize) { int sparse_ind = 0; char *buf; int buf_size = RECORDSIZE; union record *datarec; int err; long numbytes; #if 0 int amt_read = 0; #endif int size = filesize; /* FIXME: `datarec' might be used uninitialized in this function. Reported by Bruno Haible. */ buf = (char *) tar_xmalloc (buf_size * sizeof (char)); fill_in_sparse_array (); while (size > 0) { datarec = findrec (); if (!sparsearray[sparse_ind].numbytes) break; /* `numbytes' is nicer to write than `sparsearray[sparse_ind].numbytes' all the time... */ numbytes = sparsearray[sparse_ind].numbytes; lseek (diff_fd, sparsearray[sparse_ind].offset, 0); /* Take care to not run out of room in our buffer. */ while (buf_size < numbytes) { buf_size *= 2; buf = (char *) tar_realloc (buf, buf_size * sizeof (char)); } while (numbytes > RECORDSIZE) { if (err = read (diff_fd, buf, RECORDSIZE), err != RECORDSIZE) { if (err < 0) WARN ((0, errno, _("Cannot read %s"), current_file_name)); else fprintf (stdlis, _("%s: Could only read %d of %ld bytes\n"), current_file_name, err, numbytes); break; } if (memcmp (buf, datarec->charptr, RECORDSIZE)) { different++; break; } numbytes -= err; size -= err; userec (datarec); datarec = findrec (); } if (err = read (diff_fd, buf, (size_t) numbytes), err != numbytes) { if (err < 0) WARN ((0, errno, _("Cannot read %s"), current_file_name)); else fprintf (stdlis, _("%s: Could only read %d of %ld bytes\n"), current_file_name, err, numbytes); break; } if (memcmp (buf, datarec->charptr, (size_t) numbytes)) { different++; break; } #if 0 amt_read += numbytes; if (amt_read >= RECORDSIZE) { amt_read = 0; userec (datarec); datarec = findrec (); } #endif userec (datarec); sparse_ind++; size -= numbytes; } /* If the number of bytes read isn't the number of bytes supposedly in the file, they're different. */ #if 0 if (amt_read != filesize) different++; #endif userec (datarec); free (sparsearray); if (different) { fprintf (stdlis, _("%s: Data differs\n"), current_file_name); if (exit_status == TAREXIT_SUCCESS) exit_status = TAREXIT_DIFFERS; } }