summaryrefslogblamecommitdiff
path: root/src/apk_adb.c
blob: ea27a4d624c62b96b02e02780e01d166e67c0d2b (plain) (tree)





























                                                                                    
                                                         





                                                                                    
                                                             





















































                                                                                         




                                                                                       



                                                 
                                  

  







                                                  





                                                      





                                                                                 











                                                                                        
                                         



                                   












                                                                                          
















                                                                   


                                                  
                                         
                                  










                                                                                      
                                                

 








                                                                                    



                                              
                               






                                                                                      






                                                               


                                              
                                     
                               









                                                                                        












                                                                 


                                                
                                       
                               



















































































                                                                                   
                                       

 
                                                                             
 
                                                 









                                                                               
                                                                                











                                                                            

                                                      













                                                          
                                                                          

              
                                             
                        


                                                     







                                                                               
                                                                                
                                                                                















                                                                                         
                                                                            


















                                                                                      









                                                                                     
 
                                                                       
 
                                                




                                              


                                                                               


                                                                            
                                                                                
                                                                                









                                                    
                                             
                                
                                  

                            


                                                                                   


          
                                                   


                                             
                                             





                                                 






                                                                                 


          




                                                                          


                                                 
                               

                                                                                
                                                                                  








                                                                                     
                                  



                                                           
                                  










                                                                                          
#include <errno.h>
#include "adb.h"
#include "apk_adb.h"
#include "apk_print.h"
#include "apk_version.h"

#define APK_VERSION_CONFLICT 16

/* Few helpers to map old database to new one */

int apk_dep_split(apk_blob_t *b, apk_blob_t *bdep)
{
	extern const apk_spn_match_def apk_spn_dependency_separator;

	if (APK_BLOB_IS_NULL(*b)) return 0;
	if (apk_blob_cspn(*b, apk_spn_dependency_separator, bdep, b)) {
		/* found separator - update b to skip over after all separators */
		if (!apk_blob_spn(*b, apk_spn_dependency_separator, NULL, b))
			*b = APK_BLOB_NULL;
	} else {
		/* no separator - return this as the last dependency, signal quit */
		*bdep = *b;
		*b = APK_BLOB_NULL;
	}
	return 1;
}

adb_val_t adb_wo_pkginfo(struct adb_obj *obj, unsigned int f, apk_blob_t val)
{
	struct apk_checksum csum;
	adb_val_t v = ADB_ERROR(APKE_ADB_PACKAGE_FORMAT);

	/* FIXME: get rid of this function, and handle the conversion via schema? */
	switch (f) {
	case ADBI_PI_UNIQUE_ID:
		if (!val.ptr || val.len < 4) break;
		apk_blob_pull_csum(&val, &csum);
		v = adb_w_blob(obj->db, APK_BLOB_CSUM(csum));
		break;
	case ADBI_PI_REPO_COMMIT:
		if (val.len < 40) break;
		csum.type = 20;
		apk_blob_pull_hexdump(&val, APK_BLOB_CSUM(csum));
		if (val.ptr) v = adb_w_blob(obj->db, APK_BLOB_CSUM(csum));
		break;
	default:
		return adb_wo_val_fromstring(obj, f, val);
	}
	if (v != ADB_NULL && !ADB_IS_ERROR(v))
		v = adb_wo_val(obj, f, v);
	return v;
}

unsigned int adb_pkg_field_index(char f)
{
#define MAP(ch, ndx) [ch - 'A'] = ndx
	static unsigned char map[] = {
		MAP('C', ADBI_PI_UNIQUE_ID),
		MAP('P', ADBI_PI_NAME),
		MAP('V', ADBI_PI_VERSION),
		MAP('T', ADBI_PI_DESCRIPTION),
		MAP('U', ADBI_PI_URL),
		MAP('I', ADBI_PI_INSTALLED_SIZE),
		MAP('S', ADBI_PI_FILE_SIZE),
		MAP('L', ADBI_PI_LICENSE),
		MAP('A', ADBI_PI_ARCH),
		MAP('D', ADBI_PI_DEPENDS),
		MAP('i', ADBI_PI_INSTALL_IF),
		MAP('p', ADBI_PI_PROVIDES),
		MAP('o', ADBI_PI_ORIGIN),
		MAP('m', ADBI_PI_MAINTAINER),
		MAP('t', ADBI_PI_BUILD_TIME),
		MAP('c', ADBI_PI_REPO_COMMIT),
		MAP('r', ADBI_PI_REPLACES),
		MAP('k', ADBI_PI_PRIORITY),
	};
	if (f < 'A' || f-'A' >= ARRAY_SIZE(map)) return 0;
	return map[(unsigned char)f - 'A'];
}

/* Schema */

static apk_blob_t string_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
	return adb_r_blob(db, val);
}

static adb_val_t string_fromstring(struct adb *db, apk_blob_t val)
{
	return adb_w_blob(db, val);
}

static int string_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2)
{
	return apk_blob_sort(adb_r_blob(db1, v1), adb_r_blob(db2, v2));
}

static struct adb_scalar_schema scalar_string = {
	.kind = ADB_KIND_BLOB,
	.tostring = string_tostring,
	.fromstring = string_fromstring,
	.compare = string_compare,
};

static struct adb_scalar_schema scalar_mstring = {
	.kind = ADB_KIND_BLOB,
	.multiline = 1,
	.tostring = string_tostring,
	.fromstring = string_fromstring,
	.compare = string_compare,
};

const struct adb_object_schema schema_string_array = {
	.kind = ADB_KIND_ARRAY,
	.num_fields = APK_MAX_PKG_TRIGGERS,
	.fields = ADB_ARRAY_ITEM(scalar_string),
};

static adb_val_t version_fromstring(struct adb *db, apk_blob_t val)
{
	if (!apk_version_validate(val)) return ADB_ERROR(APKE_PKGVERSION_FORMAT);
	return adb_w_blob(db, val);
}

static int version_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2)
{
	switch (apk_version_compare_blob(adb_r_blob(db1, v1), adb_r_blob(db2, v2))) {
	case APK_VERSION_LESS: return -1;
	case APK_VERSION_GREATER: return 1;
	default: return 0;
	}
}

static struct adb_scalar_schema scalar_version = {
	.kind = ADB_KIND_BLOB,
	.tostring = string_tostring,
	.fromstring = version_fromstring,
	.compare = version_compare,
};


static apk_blob_t hexblob_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
	apk_blob_t b = adb_r_blob(db, val), to = APK_BLOB_PTR_LEN(buf, bufsz);

	if (APK_BLOB_IS_NULL(b)) return b;

	apk_blob_push_hexdump(&to, b);
	if (!APK_BLOB_IS_NULL(to))
		return APK_BLOB_PTR_PTR(buf, to.ptr-1);

	return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "(%ld bytes)", b.len));
}

static adb_val_t hexblob_fromstring(struct adb *db, apk_blob_t val)
{
	char buf[256];

	if (val.len & 1)
		return ADB_ERROR(EINVAL);
	if (val.len > sizeof buf)
		return ADB_ERROR(E2BIG);

	apk_blob_t b = APK_BLOB_PTR_LEN(buf, val.len / 2);
	apk_blob_pull_hexdump(&val, b);
	if (APK_BLOB_IS_NULL(val))
		return ADB_ERROR(EINVAL);

	return adb_w_blob(db, b);
}

static struct adb_scalar_schema scalar_hexblob = {
	.kind = ADB_KIND_BLOB,
	.tostring = hexblob_tostring,
	.fromstring = hexblob_fromstring,
	.compare = string_compare,
};

static apk_blob_t int_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
	return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%u", adb_r_int(db, val)));
}

static adb_val_t int_fromstring(struct adb *db, apk_blob_t val)
{
	uint32_t n = apk_blob_pull_uint(&val, 10);
	if (val.len) return ADB_ERROR(EINVAL);
	return adb_w_int(db, n) ?: ADB_VAL_NULL;
}

static int int_compare(struct adb *db1, adb_val_t v1, struct adb *db2, adb_val_t v2)
{
	uint32_t r1 = adb_r_int(db1, v1);
	uint32_t r2 = adb_r_int(db1, v2);
	if (r1 < r2) return -1;
	if (r1 > r2) return 1;
	return 0;
}

static struct adb_scalar_schema scalar_int = {
	.kind = ADB_KIND_INT,
	.tostring = int_tostring,
	.fromstring = int_fromstring,
	.compare = int_compare,
};

static apk_blob_t oct_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
	return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%o", adb_r_int(db, val)));
}

static adb_val_t oct_fromstring(struct adb *db, apk_blob_t val)
{
	uint32_t n = apk_blob_pull_uint(&val, 8);
	if (val.len) return ADB_ERROR(EINVAL);
	return adb_w_int(db, n) ?: ADB_VAL_NULL;
}

static struct adb_scalar_schema scalar_oct = {
	.kind = ADB_KIND_INT,
	.tostring = oct_tostring,
	.fromstring = oct_fromstring,
	.compare = int_compare,
};

static apk_blob_t hsize_tostring(struct adb *db, adb_val_t val, char *buf, size_t bufsz)
{
	off_t v = adb_r_int(db, val);
	const char *unit = apk_get_human_size(v, &v);

	return APK_BLOB_PTR_LEN(buf, snprintf(buf, bufsz, "%jd %s", (intmax_t)v, unit));
}

static adb_val_t hsize_fromstring(struct adb *db, apk_blob_t val)
{
	apk_blob_t l, r;

	if (!apk_blob_split(val, APK_BLOB_STR(" "), &l, &r))
		return int_fromstring(db, val);

	uint64_t n = apk_blob_pull_uint(&l, 10);
	int sz = apk_get_human_size_unit(r);
	n *= sz;
	return adb_w_int(db, n);
}

static struct adb_scalar_schema scalar_hsize = {
	.kind = ADB_KIND_INT,
	.tostring = hsize_tostring,
	.fromstring = hsize_fromstring,
	.compare = int_compare,
};

static apk_blob_t dependency_tostring(struct adb_obj *obj, char *buf, size_t bufsz)
{
	apk_blob_t name, ver;
	unsigned int mask;

	name = adb_ro_blob(obj, ADBI_DEP_NAME);
	ver  = adb_ro_blob(obj, ADBI_DEP_VERSION);

	if (APK_BLOB_IS_NULL(name)) return APK_BLOB_NULL;
	if (APK_BLOB_IS_NULL(ver)) return name;

	mask = adb_ro_int(obj, ADBI_DEP_MATCH) ?: APK_VERSION_EQUAL;
	return APK_BLOB_PTR_LEN(buf,
		snprintf(buf, bufsz, "%s"BLOB_FMT"%s"BLOB_FMT,
			(mask & APK_VERSION_CONFLICT) ? "!" : "",
			BLOB_PRINTF(name),
			apk_version_op_string(mask & ~APK_VERSION_CONFLICT),
			BLOB_PRINTF(ver)));
}

static int dependency_fromstring(struct adb_obj *obj, apk_blob_t bdep)
{
	extern const apk_spn_match_def apk_spn_dependency_comparer;
	extern const apk_spn_match_def apk_spn_repotag_separator;
	apk_blob_t bname, bop, bver = APK_BLOB_NULL, btag;
	int mask = APK_DEPMASK_ANY;

	/* [!]name[<,<=,<~,=,~,>~,>=,>,><]ver */

	/* parse the version */
	if (bdep.ptr[0] == '!') {
		bdep.ptr++;
		bdep.len--;
		mask |= APK_VERSION_CONFLICT;
	}

	if (apk_blob_cspn(bdep, apk_spn_dependency_comparer, &bname, &bop)) {
		int i;

		if (mask == 0)
			goto fail;
		if (!apk_blob_spn(bop, apk_spn_dependency_comparer, &bop, &bver))
			goto fail;

		mask = 0;
		for (i = 0; i < bop.len; i++) {
			switch (bop.ptr[i]) {
			case '<':
				mask |= APK_VERSION_LESS;
				break;
			case '>':
				mask |= APK_VERSION_GREATER;
				break;
			case '~':
				mask |= APK_VERSION_FUZZY|APK_VERSION_EQUAL;
				break;
			case '=':
				mask |= APK_VERSION_EQUAL;
				break;
			}
		}
		if ((mask & APK_DEPMASK_CHECKSUM) != APK_DEPMASK_CHECKSUM &&
		    !apk_version_validate(bver))
			goto fail;
	} else {
		bname = bdep;
		bop = APK_BLOB_NULL;
		bver = APK_BLOB_NULL;
	}

	if (apk_blob_cspn(bname, apk_spn_repotag_separator, &bname, &btag))
		; /* tag = repository tag */

	adb_wo_blob(obj, ADBI_DEP_NAME, bname);
	if (mask != APK_DEPMASK_ANY) {
		adb_wo_blob(obj, ADBI_DEP_VERSION, bver);
		if (mask != APK_VERSION_EQUAL)
			adb_wo_int(obj, ADBI_DEP_MATCH, mask);
	}
	return 0;

fail:
	return -APKE_DEPENDENCY_FORMAT;
}

static int dependency_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
{
	return adb_ro_cmp(o1, o2, ADBI_DEP_NAME);
}

const struct adb_object_schema schema_dependency = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_DEP_MAX,
	.tostring = dependency_tostring,
	.fromstring = dependency_fromstring,
	.compare = dependency_cmp,
	.fields = {
		ADB_FIELD(ADBI_DEP_NAME,	"name",		scalar_string),
		ADB_FIELD(ADBI_DEP_VERSION,	"version",	scalar_version),
		ADB_FIELD(ADBI_DEP_MATCH,	"match",	scalar_int),
	},
};

static int dependencies_fromstring(struct adb_obj *obj, apk_blob_t b)
{
	struct adb_obj dep;
	apk_blob_t bdep;

	adb_wo_alloca(&dep, &schema_dependency, obj->db);

	while (apk_dep_split(&b, &bdep)) {
		int r = adb_wo_fromstring(&dep, bdep);
		if (r) return r;
		adb_wa_append_obj(obj, &dep);
	}

	return 0;
}

const struct adb_object_schema schema_dependency_array = {
	.kind = ADB_KIND_ARRAY,
	.fromstring = dependencies_fromstring,
	.num_fields = APK_MAX_PKG_DEPENDENCIES,
	.pre_commit = adb_wa_sort_unique,
	.fields = ADB_ARRAY_ITEM(schema_dependency),
};

static int pkginfo_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
{
	int r;
	r = adb_ro_cmp(o1, o2, ADBI_PI_NAME);
	if (r) return r;
	r = adb_ro_cmp(o1, o2, ADBI_PI_VERSION);
	if (r) return r;
	return adb_ro_cmp(o1, o2, ADBI_PI_UNIQUE_ID);
}

const struct adb_object_schema schema_pkginfo = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_PI_MAX,
	.compare = pkginfo_cmp,
	.fields = {
		ADB_FIELD(ADBI_PI_NAME,		"name",		scalar_string),
		ADB_FIELD(ADBI_PI_VERSION,	"version",	scalar_version),
		ADB_FIELD(ADBI_PI_UNIQUE_ID,	"unique-id",	scalar_hexblob),
		ADB_FIELD(ADBI_PI_DESCRIPTION,	"description",	scalar_string),
		ADB_FIELD(ADBI_PI_ARCH,		"arch",		scalar_string),
		ADB_FIELD(ADBI_PI_LICENSE,	"license",	scalar_string),
		ADB_FIELD(ADBI_PI_ORIGIN,	"origin",	scalar_string),
		ADB_FIELD(ADBI_PI_MAINTAINER,	"maintainer",	scalar_string),
		ADB_FIELD(ADBI_PI_URL,		"url",		scalar_string),
		ADB_FIELD(ADBI_PI_REPO_COMMIT,	"repo-commit",	scalar_hexblob),
		ADB_FIELD(ADBI_PI_BUILD_TIME,	"build-time",	scalar_int),
		ADB_FIELD(ADBI_PI_INSTALLED_SIZE,"installed-size",scalar_hsize),
		ADB_FIELD(ADBI_PI_FILE_SIZE,	"file-size",	scalar_hsize),
		ADB_FIELD(ADBI_PI_PRIORITY,	"priority",	scalar_int),
		ADB_FIELD(ADBI_PI_DEPENDS,	"depends",	schema_dependency_array),
		ADB_FIELD(ADBI_PI_PROVIDES,	"provides",	schema_dependency_array),
		ADB_FIELD(ADBI_PI_REPLACES,	"replaces",	schema_dependency_array),
		ADB_FIELD(ADBI_PI_INSTALL_IF,	"install-if",	schema_dependency_array),
		ADB_FIELD(ADBI_PI_RECOMMENDS,	"recommends",	schema_dependency_array),
		ADB_FIELD(ADBI_PI_LAYER,	"layer",	scalar_int),
	},
};

const struct adb_object_schema schema_pkginfo_array = {
	.kind = ADB_KIND_ARRAY,
	.num_fields = APK_MAX_INDEX_PACKAGES,
	.pre_commit = adb_wa_sort,
	.fields = ADB_ARRAY_ITEM(schema_pkginfo),
};

const struct adb_object_schema schema_index = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_NDX_MAX,
	.fields = {
		ADB_FIELD(ADBI_NDX_DESCRIPTION,	"description",	scalar_string),
		ADB_FIELD(ADBI_NDX_PACKAGES,	"packages",	schema_pkginfo_array),
	},
};

const struct adb_object_schema schema_acl = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_ACL_MAX,
	.fields = {
		ADB_FIELD(ADBI_ACL_MODE,	"mode",		scalar_oct),
		ADB_FIELD(ADBI_ACL_USER,	"user",		scalar_string),
		ADB_FIELD(ADBI_ACL_GROUP,	"group",	scalar_string),
		//ADB_FIELD(ADBI_ACL_XATTRS,	"xattr",	schema_string_array),
	},
};

static int file_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
{
	return adb_ro_cmp(o1, o2, ADBI_FI_NAME);
}

const struct adb_object_schema schema_file = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_FI_MAX,
	.compare = file_cmp,
	.fields = {
		ADB_FIELD(ADBI_FI_NAME,		"name",		scalar_string),
		ADB_FIELD(ADBI_FI_ACL,		"acl",		schema_acl),
		ADB_FIELD(ADBI_FI_SIZE,		"size",		scalar_int),
		ADB_FIELD(ADBI_FI_MTIME,	"mtime",	scalar_int),
		ADB_FIELD(ADBI_FI_HASHES,	"hash",		scalar_hexblob),
		ADB_FIELD(ADBI_FI_TARGET,	"target",	scalar_hexblob),
	},
};

const struct adb_object_schema schema_file_array = {
	.kind = ADB_KIND_ARRAY,
	.pre_commit = adb_wa_sort,
	.num_fields = APK_MAX_MANIFEST_FILES,
	.fields = ADB_ARRAY_ITEM(schema_file),
};

const struct adb_object_schema schema_dir = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_DI_MAX,
	.compare = file_cmp,
	.fields = {
		ADB_FIELD(ADBI_DI_NAME,		"name",		scalar_string),
		ADB_FIELD(ADBI_DI_ACL,		"acl",		schema_acl),
		ADB_FIELD(ADBI_DI_FILES,	"files",	schema_file_array),
	},
};

const struct adb_object_schema schema_dir_array = {
	.kind = ADB_KIND_ARRAY,
	.pre_commit = adb_wa_sort,
	.num_fields = APK_MAX_MANIFEST_PATHS,
	.fields = ADB_ARRAY_ITEM(schema_dir),
};

const struct adb_object_schema schema_scripts = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_SCRPT_MAX,
	.fields = {
		ADB_FIELD(ADBI_SCRPT_TRIGGER,	"trigger",	scalar_mstring),
		ADB_FIELD(ADBI_SCRPT_PREINST,	"pre-install",	scalar_mstring),
		ADB_FIELD(ADBI_SCRPT_POSTINST,	"post-install",	scalar_mstring),
		ADB_FIELD(ADBI_SCRPT_PREDEINST,	"pre-deinstall",scalar_mstring),
		ADB_FIELD(ADBI_SCRPT_POSTDEINST,"post-deinstall",scalar_mstring),
		ADB_FIELD(ADBI_SCRPT_PREUPGRADE,"pre-upgrade",	scalar_mstring),
		ADB_FIELD(ADBI_SCRPT_POSTUPGRADE,"post-upgrade",scalar_mstring),
	},
};

static int package_cmp(const struct adb_obj *o1, const struct adb_obj *o2)
{
	return adb_ro_cmp(o1, o2, ADBI_PKG_PKGINFO);
}

const struct adb_object_schema schema_package = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_PKG_MAX,
	.compare = package_cmp,
	.fields = {
		ADB_FIELD(ADBI_PKG_PKGINFO,	"info",		schema_pkginfo),
		ADB_FIELD(ADBI_PKG_PATHS,	"paths",	schema_dir_array),
		ADB_FIELD(ADBI_PKG_SCRIPTS,	"scripts",	schema_scripts),
		ADB_FIELD(ADBI_PKG_TRIGGERS,	"triggers",	schema_string_array),
		//ADB_FIELD(ADBI_PKG_PASSWD,	"passwd",	schema_string_array),
	},
};

const struct adb_adb_schema schema_package_adb = {
	.kind = ADB_KIND_ADB,
	.schema_id = ADB_SCHEMA_PACKAGE,
	.schema = &schema_package,
};

const struct adb_object_schema schema_package_adb_array = {
	.kind = ADB_KIND_ARRAY,
	.pre_commit = adb_wa_sort,
	.num_fields = APK_MAX_INDEX_PACKAGES,
	.fields = ADB_ARRAY_ITEM(schema_package_adb),
};

const struct adb_object_schema schema_idb = {
	.kind = ADB_KIND_OBJECT,
	.num_fields = ADBI_IDB_MAX,
	.fields = {
		ADB_FIELD(ADBI_IDB_PACKAGES,	"packages",	schema_package_adb_array),
	},
};