pkgtools

morpheus pkg tools
git clone git://git.2f30.org/pkgtools.git
Log | Files | Refs | README | LICENSE

pkg.c (7332B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include "pkg.h"
      3 
      4 /* Create a package from the db entry.  e.g. /var/pkg/pkg#version */
      5 struct pkg *
      6 pkg_load(struct db *db, const char *file)
      7 {
      8 	struct pkg *pkg;
      9 	struct pkgentry *pe;
     10 	FILE *fp;
     11 	char path[PATH_MAX];
     12 	char *name, *version;
     13 	char *buf = NULL;
     14 	size_t sz = 0;
     15 	ssize_t len;
     16 
     17 	parse_db_name(file, &name);
     18 	parse_db_version(file, &version);
     19 	estrlcpy(path, db->path, sizeof(path));
     20 	estrlcat(path, "/", sizeof(path));
     21 	estrlcat(path, name, sizeof(path));
     22 	if (version) {
     23 		estrlcat(path, "#", sizeof(path));
     24 		estrlcat(path, version, sizeof(path));
     25 	}
     26 	pkg = pkg_new(path, name, version);
     27 	free(name);
     28 	free(version);
     29 
     30 	if (!(fp = fopen(pkg->path, "r"))) {
     31 		weprintf("fopen %s:", pkg->path);
     32 		pkg_free(pkg);
     33 		return NULL;
     34 	}
     35 
     36 	while ((len = getline(&buf, &sz, fp)) != -1) {
     37 		if (len > 0 && buf[len - 1] == '\n')
     38 			buf[len - 1] = '\0';
     39 
     40 		if (buf[0] == '\0') {
     41 			weprintf("%s: malformed pkg file\n", pkg->path);
     42 			free(buf);
     43 			fclose(fp);
     44 			pkg_free(pkg);
     45 			return NULL;
     46 		}
     47 
     48 		pe = pkgentry_new(db, buf);
     49 		TAILQ_INSERT_TAIL(&pkg->pe_head, pe, entry);
     50 	}
     51 
     52 	if (ferror(fp)) {
     53 		weprintf("%s: read error:", pkg->name);
     54 		free(buf);
     55 		fclose(fp);
     56 		pkg_free(pkg);
     57 		return NULL;
     58 	}
     59 
     60 	free(buf);
     61 	fclose(fp);
     62 
     63 	return pkg;
     64 }
     65 
     66 /* Create a package from a file.  e.g. /tmp/pkg#version.pkg.tgz */
     67 struct pkg *
     68 pkg_load_file(struct db *db, const char *file)
     69 {
     70 	struct pkg *pkg;
     71 	struct pkgentry *pe;
     72 	struct archive *ar;
     73 	struct archive_entry *entry;
     74 	char path[PATH_MAX];
     75 	const char *tmp;
     76 	char *name, *version;
     77 	int r;
     78 
     79 	if (!realpath(file, path)) {
     80 		weprintf("realpath %s:", file);
     81 		return NULL;
     82 	}
     83 
     84 	parse_name(path, &name);
     85 	parse_version(path, &version);
     86 	pkg = pkg_new(path, name, version);
     87 	free(name);
     88 	free(version);
     89 
     90 	ar = archive_read_new();
     91 
     92 	archive_read_support_filter_gzip(ar);
     93 	archive_read_support_filter_bzip2(ar);
     94 	archive_read_support_filter_xz(ar);
     95 	archive_read_support_format_tar(ar);
     96 
     97 	if (archive_read_open_filename(ar, pkg->path, ARCHIVEBUFSIZ) < 0) {
     98 		weprintf("archive_read_open_filename %s: %s\n", pkg->path,
     99 			 archive_error_string(ar));
    100 		archive_read_free(ar);
    101 		pkg_free(pkg);
    102 		return NULL;
    103 	}
    104 
    105 	while (1) {
    106 		r = archive_read_next_header(ar, &entry);
    107 		if (r == ARCHIVE_EOF)
    108 			break;
    109 		if (r != ARCHIVE_OK) {
    110 			weprintf("archive_read_next_header: %s\n",
    111 				 archive_error_string(ar));
    112 			archive_read_free(ar);
    113 			pkg_free(pkg);
    114 			return NULL;
    115 		}
    116 
    117 		tmp = archive_entry_pathname(entry);
    118 		if (strncmp(tmp, "./", 2) == 0)
    119 			tmp += 2;
    120 
    121 		if (tmp[0] == '\0')
    122 			continue;
    123 
    124 		pe = pkgentry_new(db, tmp);
    125 		TAILQ_INSERT_TAIL(&pkg->pe_head, pe, entry);
    126 	}
    127 
    128 	archive_read_free(ar);
    129 
    130 	return pkg;
    131 }
    132 
    133 int
    134 pkg_install(struct db *db, struct pkg *pkg)
    135 {
    136 	struct archive *ar;
    137 	struct archive_entry *entry;
    138 	char cwd[PATH_MAX];
    139 	int flags, r;
    140 
    141 	ar = archive_read_new();
    142 
    143 	archive_read_support_filter_gzip(ar);
    144 	archive_read_support_filter_bzip2(ar);
    145 	archive_read_support_filter_xz(ar);
    146 	archive_read_support_format_tar(ar);
    147 
    148 	if (archive_read_open_filename(ar, pkg->path, ARCHIVEBUFSIZ) < 0) {
    149 		weprintf("archive_read_open_filename %s: %s\n", pkg->path,
    150 			 archive_error_string(ar));
    151 		archive_read_free(ar);
    152 		return -1;
    153 	}
    154 
    155 	if (!getcwd(cwd, sizeof(cwd))) {
    156 		weprintf("getcwd:");
    157 		archive_read_free(ar);
    158 		return -1;
    159 	}
    160 	if (chdir(db->root) < 0) {
    161 		weprintf("chdir %s:", db->root);
    162 		archive_read_free(ar);
    163 		return -1;
    164 	}
    165 
    166 	while (1) {
    167 		r = archive_read_next_header(ar, &entry);
    168 		if (r == ARCHIVE_EOF)
    169 			break;
    170 		if (r != ARCHIVE_OK) {
    171 			weprintf("archive_read_next_header %s: %s\n",
    172 				 archive_entry_pathname(entry), archive_error_string(ar));
    173 			if (chdir(cwd) < 0)
    174 				weprintf("chdir %s:", cwd);
    175 			archive_read_free(ar);
    176 			return -1;
    177 		}
    178 		if (rej_match(db, archive_entry_pathname(entry)) > 0) {
    179 			weprintf("rejecting %s\n", archive_entry_pathname(entry));
    180 			continue;
    181 		}
    182 		flags = ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM |
    183 			ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_NODOTDOT;
    184 		if (fflag == 1)
    185 			flags |= ARCHIVE_EXTRACT_UNLINK;
    186 		r = archive_read_extract(ar, entry, flags);
    187 		if (r != ARCHIVE_OK && r != ARCHIVE_WARN)
    188 			weprintf("archive_read_extract %s: %s\n",
    189 				 archive_entry_pathname(entry), archive_error_string(ar));
    190 	}
    191 
    192 	archive_read_free(ar);
    193 
    194 	if (chdir(cwd) < 0) {
    195 		weprintf("chdir %s:", cwd);
    196 		return -1;
    197 	}
    198 
    199 	return 0;
    200 }
    201 
    202 static int
    203 rm_empty_dir(const char *f, const struct stat *sb, int typeflag,
    204 	   struct FTW *ftwbuf)
    205 {
    206 	(void) sb;
    207 	(void) ftwbuf;
    208 
    209 	if (typeflag == FTW_DP) {
    210 		if (vflag == 1)
    211 			printf("removing %s\n", f);
    212 		rmdir(f);
    213 	}
    214 	return 0;
    215 }
    216 
    217 int
    218 pkg_remove(struct db *db, struct pkg *pkg)
    219 {
    220 	struct pkgentry *pe;
    221 	struct stat sb;
    222 
    223 	TAILQ_FOREACH_REVERSE(pe, &pkg->pe_head, pe_head, entry) {
    224 		if (rej_match(db, pe->rpath) > 0) {
    225 			weprintf("rejecting %s\n", pe->rpath);
    226 			continue;
    227 		}
    228 
    229 		if (lstat(pe->path, &sb) < 0) {
    230 			weprintf("lstat %s:", pe->path);
    231 			continue;
    232 		}
    233 
    234 		if (S_ISDIR(sb.st_mode) == 1) {
    235 			if (fflag == 0)
    236 				printf("ignoring directory %s\n", pe->path);
    237 			/* We'll remove these further down in a separate pass */
    238 			continue;
    239 		}
    240 
    241 		if (S_ISLNK(sb.st_mode) == 1) {
    242 			if (fflag == 0) {
    243 				printf("ignoring link %s\n", pe->path);
    244 				continue;
    245 			}
    246 		}
    247 
    248 		if (vflag == 1)
    249 			printf("removing %s\n", pe->path);
    250 		if (remove(pe->path) < 0)
    251 			weprintf("remove %s:", pe->path);
    252 	}
    253 
    254 	if (fflag == 1) {
    255 		/* prune empty directories as well */
    256 		TAILQ_FOREACH_REVERSE(pe, &pkg->pe_head, pe_head, entry) {
    257 			if (rej_match(db, pe->rpath) > 0)
    258 				continue;
    259 			if (db_links(db, pe->path) > 1)
    260 				continue;
    261 			nftw(pe->path, rm_empty_dir, 1, FTW_DEPTH);
    262 		}
    263 	}
    264 
    265 	TAILQ_REMOVE(&db->pkg_head, pkg, entry);
    266 	TAILQ_INSERT_TAIL(&db->pkg_rm_head, pkg, entry);
    267 
    268 	return 0;
    269 }
    270 
    271 /* Check if the file entries of the package
    272  * collide with corresponding entries in the filesystem */
    273 int
    274 pkg_collisions(struct pkg *pkg)
    275 {
    276 	struct pkgentry *pe;
    277 	struct stat sb;
    278 	char resolvedpath[PATH_MAX];
    279 	int r = 0;
    280 
    281 	TAILQ_FOREACH(pe, &pkg->pe_head, entry) {
    282 		if (access(pe->path, F_OK) == 0) {
    283 			if (stat(pe->path, &sb) < 0) {
    284 				weprintf("lstat %s:", pe->path);
    285 				return -1;
    286 			}
    287 			if (S_ISDIR(sb.st_mode) == 0) {
    288 				if (realpath(pe->path, resolvedpath))
    289 					weprintf("%s exists\n", resolvedpath);
    290 				else
    291 					weprintf("%s exists\n", pe->path);
    292 				r = -1;
    293 			}
    294 		}
    295 	}
    296 
    297 	return r;
    298 }
    299 
    300 struct pkg *
    301 pkg_new(const char *path, const char *name, const char *version)
    302 {
    303 	struct pkg *pkg;
    304 
    305 	pkg = emalloc(sizeof(*pkg));
    306 	pkg->name = estrdup(name);
    307 	if (version)
    308 		pkg->version = estrdup(version);
    309 	else
    310 		pkg->version = NULL;
    311 	estrlcpy(pkg->path, path, sizeof(pkg->path));
    312 	TAILQ_INIT(&pkg->pe_head);
    313 	return pkg;
    314 }
    315 
    316 void
    317 pkg_free(struct pkg *pkg)
    318 {
    319 	struct pkgentry *pe, *tmp;
    320 
    321 	for (pe = TAILQ_FIRST(&pkg->pe_head); pe; pe = tmp) {
    322 		tmp = TAILQ_NEXT(pe, entry);
    323 		TAILQ_REMOVE(&pkg->pe_head, pe, entry);
    324 		pkgentry_free(pe);
    325 	}
    326 	free(pkg->name);
    327 	free(pkg->version);
    328 	free(pkg);
    329 }
    330 
    331 struct pkgentry *
    332 pkgentry_new(struct db *db, const char *file)
    333 {
    334 	struct pkgentry *pe;
    335 
    336 	pe = emalloc(sizeof(*pe));
    337 	estrlcpy(pe->path, db->root, sizeof(pe->path));
    338 	estrlcat(pe->path, "/", sizeof(pe->path));
    339 	estrlcat(pe->path, file, sizeof(pe->path));
    340 	estrlcpy(pe->rpath, file, sizeof(pe->rpath));
    341 	return pe;
    342 }
    343 
    344 void
    345 pkgentry_free(struct pkgentry *pe)
    346 {
    347 	free(pe);
    348 }