blob: 83bda995dd64f0c73ef5f9d06b7204b13e697081 [file] [log] [blame]
#include "fdiskP.h"
#include "strutils.h"
/**
* SECTION: script
* @title: Script
* @short_description: text based sfdisk compatible description of partition table
*
* The libfdisk scripts are based on original sfdisk script (dumps). Each
* script has two parts: script headers and partition table entries
* (partitions).
*
* For more details about script format see sfdisk man page.
*/
/* script header (e.g. unit: sectors) */
struct fdisk_scriptheader {
struct list_head headers;
char *name;
char *data;
};
/* script control struct */
struct fdisk_script {
struct fdisk_table *table;
struct list_head headers;
struct fdisk_context *cxt;
int refcount;
/* parser's state */
size_t nlines;
int fmt; /* input format */
struct fdisk_label *label;
};
static void fdisk_script_free_header(struct fdisk_script *dp, struct fdisk_scriptheader *fi)
{
if (!fi)
return;
DBG(SCRIPT, ul_debugobj(fi, "free header %s", fi->name));
free(fi->name);
free(fi->data);
list_del(&fi->headers);
free(fi);
}
/**
* fdisk_new_script:
* @cxt: context
*
* The script hold fdisk_table and additional information to read/write
* script to the file.
*
* Returns: newly allocated script struct.
*/
struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt)
{
struct fdisk_script *dp = NULL;
dp = calloc(1, sizeof(*dp));
if (!dp)
return NULL;
DBG(SCRIPT, ul_debugobj(dp, "alloc"));
dp->refcount = 1;
dp->cxt = cxt;
fdisk_ref_context(cxt);
dp->table = fdisk_new_table();
if (!dp->table) {
fdisk_unref_script(dp);
return NULL;
}
INIT_LIST_HEAD(&dp->headers);
return dp;
}
/**
* fdisk_new_script_from_file:
* @cxt: context
* @filename: path to the script file
*
* Allocates a new script and reads script from @filename.
*
* Returns: new script instance or NULL in case of error (check errno for more details).
*/
struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
const char *filename)
{
int rc;
FILE *f;
struct fdisk_script *dp, *res = NULL;
assert(cxt);
assert(filename);
DBG(SCRIPT, ul_debug("opening %s", filename));
f = fopen(filename, "r");
if (!f)
return NULL;
dp = fdisk_new_script(cxt);
if (!dp)
goto done;
rc = fdisk_script_read_file(dp, f);
if (rc) {
errno = -rc;
goto done;
}
res = dp;
done:
fclose(f);
if (!res)
fdisk_unref_script(dp);
else
errno = 0;
return res;
}
/**
* fdisk_ref_script:
* @dp: script pointer
*
* Incremparts reference counter.
*/
void fdisk_ref_script(struct fdisk_script *dp)
{
if (dp)
dp->refcount++;
}
static void fdisk_reset_script(struct fdisk_script *dp)
{
assert(dp);
DBG(SCRIPT, ul_debugobj(dp, "reset"));
fdisk_unref_table(dp->table);
dp->table = NULL;
while (!list_empty(&dp->headers)) {
struct fdisk_scriptheader *fi = list_entry(dp->headers.next,
struct fdisk_scriptheader, headers);
fdisk_script_free_header(dp, fi);
}
INIT_LIST_HEAD(&dp->headers);
}
/**
* fdisk_unref_script:
* @dp: script pointer
*
* De-incremparts reference counter, on zero the @dp is automatically
* deallocated.
*/
void fdisk_unref_script(struct fdisk_script *dp)
{
if (!dp)
return;
dp->refcount--;
if (dp->refcount <= 0) {
fdisk_reset_script(dp);
fdisk_unref_context(dp->cxt);
DBG(SCRIPT, ul_debugobj(dp, "free script"));
free(dp);
}
}
static struct fdisk_scriptheader *script_get_header(struct fdisk_script *dp,
const char *name)
{
struct list_head *p;
list_for_each(p, &dp->headers) {
struct fdisk_scriptheader *fi = list_entry(p, struct fdisk_scriptheader, headers);
if (strcasecmp(fi->name, name) == 0)
return fi;
}
return NULL;
}
/**
* fdisk_script_get_header:
* @dp: script instance
* @name: header name
*
* Returns: pointer to header data or NULL.
*/
const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name)
{
struct fdisk_scriptheader *fi;
assert(dp);
assert(name);
fi = script_get_header(dp, name);
return fi ? fi->data : NULL;
}
/**
* fdisk_script_set_header:
* @dp: script instance
* @name: header name
* @data: header data (or NULL)
*
* The headers are used as global options (in script) for whole partition
* table, always one header per line.
*
* If no @data specified then the header is removed. If header does not exist
* and @data specified then a new header added.
*
* Note that libfdisk allows to specify arbitrary custom header, the default
* build-in headers are "unit" and "label", and some label specific headers
* (for example "uuid" and "name" for GPT).
*
* Returns: 0 on success, <0 on error
*/
int fdisk_script_set_header(struct fdisk_script *dp,
const char *name,
const char *data)
{
struct fdisk_scriptheader *fi;
assert(dp);
assert(name);
if (!dp || !name)
return -EINVAL;
fi = script_get_header(dp, name);
if (!fi && !data)
return 0; /* want to remove header that does not exist, success */
if (!data) {
/* no data, remove the header */
fdisk_script_free_header(dp, fi);
return 0;
}
if (!fi) {
/* new header */
fi = calloc(1, sizeof(*fi));
if (!fi)
return -ENOMEM;
INIT_LIST_HEAD(&fi->headers);
fi->name = strdup(name);
fi->data = strdup(data);
if (!fi->data || !fi->name) {
fdisk_script_free_header(dp, fi);
return -ENOMEM;
}
list_add_tail(&fi->headers, &dp->headers);
} else {
/* update existing */
char *x = strdup(data);
if (!x)
return -ENOMEM;
free(fi->data);
fi->data = x;
}
if (strcmp(name, "label") == 0)
dp->label = NULL;
return 0;
}
/**
* fdisk_script_get_table:
* @dp: script
*
* The table (container with partitions) is possible to create by
* fdisk_script_read_context() or fdisk_script_read_file(), otherwise
* this function returns NULL.
*
* Returns: NULL or script.
*/
struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp)
{
assert(dp);
return dp ? dp->table : NULL;
}
static struct fdisk_label *script_get_label(struct fdisk_script *dp)
{
assert(dp);
assert(dp->cxt);
if (!dp->label) {
dp->label = fdisk_get_label(dp->cxt,
fdisk_script_get_header(dp, "label"));
DBG(SCRIPT, ul_debugobj(dp, "label '%s'", dp->label ? dp->label->name : ""));
}
return dp->label;
}
/**
* fdisk_script_get_nlines:
* @dp: script
*
* Returns: number of parsed lines or <0 on error.
*/
int fdisk_script_get_nlines(struct fdisk_script *dp)
{
assert(dp);
return dp->nlines;
}
/**
* fdisk_script_read_context:
* @dp: script
* @cxt: context
*
* Reads data from the @cxt context (on disk partition table) into the script.
* If the context is no specified than defaults to context used for fdisk_new_script().
*
* Return: 0 on success, <0 on error.
*/
int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt)
{
struct fdisk_label *lb;
int rc;
char *p = NULL;
assert(dp);
if (!cxt)
cxt = dp->cxt;
if (!dp || !cxt)
return -EINVAL;
DBG(SCRIPT, ul_debugobj(dp, "reading context into script"));
fdisk_reset_script(dp);
lb = fdisk_get_label(cxt, NULL);
if (!lb)
return -EINVAL;
/* allocate and fill new table */
rc = fdisk_get_partitions(cxt, &dp->table);
if (rc)
return rc;
/* generate headers */
rc = fdisk_script_set_header(dp, "label", fdisk_label_get_name(lb));
if (!rc && fdisk_get_disklabel_id(cxt, &p) == 0 && p) {
rc = fdisk_script_set_header(dp, "label-id", p);
free(p);
}
if (!rc && cxt->dev_path)
rc = fdisk_script_set_header(dp, "device", cxt->dev_path);
if (!rc)
rc = fdisk_script_set_header(dp, "unit", "sectors");
/* TODO: label specific headers (e.g. uuid for GPT) */
DBG(SCRIPT, ul_debugobj(dp, "read context done [rc=%d]", rc));
return rc;
}
/**
* fdisk_script_write_file:
* @dp: script
* @f: output file
*
* Writes script @dp to the ile @f.
*
* Returns: 0 on success, <0 on error.
*/
int fdisk_script_write_file(struct fdisk_script *dp, FILE *f)
{
struct list_head *h;
struct fdisk_partition *pa;
struct fdisk_iter itr;
const char *devname = NULL;
assert(dp);
assert(f);
DBG(SCRIPT, ul_debugobj(dp, "writing script to file"));
/* script headers */
list_for_each(h, &dp->headers) {
struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
fprintf(f, "%s: %s\n", fi->name, fi->data);
if (strcmp(fi->name, "device") == 0)
devname = fi->data;
}
if (!dp->table) {
DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
return 0;
}
DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
fputc('\n', f);
fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
char *p = NULL;
if (devname)
p = fdisk_partname(devname, pa->partno + 1);
if (p) {
DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
fprintf(f, "%s :", p);
} else
fprintf(f, "%zu :", pa->partno + 1);
if (fdisk_partition_has_start(pa))
fprintf(f, " start=%12ju", pa->start);
if (fdisk_partition_has_size(pa))
fprintf(f, ", size=%12ju", pa->size);
if (pa->type && fdisk_parttype_get_string(pa->type))
fprintf(f, ", type=%s", fdisk_parttype_get_string(pa->type));
else if (pa->type)
fprintf(f, ", type=%x", fdisk_parttype_get_code(pa->type));
if (pa->uuid)
fprintf(f, ", uuid=%s", pa->uuid);
if (pa->name && *pa->name)
fprintf(f, ", name=\"%s\"", pa->name);
/* for MBR attr=80 means bootable */
if (pa->attrs) {
struct fdisk_label *lb = script_get_label(dp);
if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
fprintf(f, ", attrs=\"%s\"", pa->attrs);
}
if (fdisk_partition_is_bootable(pa))
fprintf(f, ", bootable");
fputc('\n', f);
}
DBG(SCRIPT, ul_debugobj(dp, "write script done"));
return 0;
}
static inline int is_header_line(const char *s)
{
const char *p = strchr(s, ':');
if (!p || p == s || !*(p + 1) || strchr(s, '='))
return 0;
return 1;
}
/* parses "<name>: value", note modifies @s*/
static int parse_header_line(struct fdisk_script *dp, char *s)
{
int rc = -EINVAL;
char *name, *value;
DBG(SCRIPT, ul_debugobj(dp, " parse header '%s'", s));
if (!s || !*s)
return -EINVAL;
name = s;
value = strchr(s, ':');
if (!value)
goto done;
*value = '\0';
value++;
ltrim_whitespace((unsigned char *) name);
rtrim_whitespace((unsigned char *) name);
ltrim_whitespace((unsigned char *) value);
rtrim_whitespace((unsigned char *) value);
if (strcmp(name, "label") == 0) {
if (dp->cxt && !fdisk_get_label(dp->cxt, value))
goto done; /* unknown label name */
} else if (strcmp(name, "unit") == 0) {
if (strcmp(value, "sectors") != 0)
goto done; /* only "sectors" supported */
} else if (strcmp(name, "label-id") == 0
|| strcmp(name, "device") == 0) {
; /* whatever is posssible */
} else
goto done; /* unknown header */
if (*name && *value)
rc = fdisk_script_set_header(dp, name, value);
done:
if (rc)
DBG(SCRIPT, ul_debugobj(dp, "header parse error: "
"[rc=%d, name='%s', value='%s']",
rc, name, value));
return rc;
}
/* returns zero terminated string with next token and @str is updated */
static char *next_token(char **str)
{
char *tk_begin = NULL,
*tk_end = NULL,
*end = NULL,
*p;
int open_quote = 0;
for (p = *str; p && *p; p++) {
if (!tk_begin) {
if (isblank(*p))
continue;
tk_begin = *p == '"' ? p + 1 : p;
}
if (*p == '"')
open_quote ^= 1;
if (open_quote)
continue;
if (isblank(*p) || *p == ',' || *p == ';' || *p == '"' )
tk_end = p;
else if (*(p + 1) == '\0')
tk_end = p + 1;
if (tk_begin && tk_end)
break;
}
if (!tk_end)
return NULL;
end = isblank(*tk_end) ? (char *) skip_blank(tk_end) : tk_end;
if (*end == ',' || *end == ';')
end++;
*tk_end = '\0';
*str = end;
return tk_begin;
}
static int next_number(char **s, uint64_t *num, int *power)
{
char *tk;
int rc = -EINVAL;
assert(num);
assert(s);
tk = next_token(s);
if (tk)
rc = parse_size(tk, (uintmax_t *) num, power);
return rc;
}
static int next_string(char **s, char **str)
{
char *tk;
int rc = -EINVAL;
assert(s);
assert(str);
tk = next_token(s);
if (tk) {
*str = strdup(tk);
rc = !*str ? -ENOMEM : 0;
}
return rc;
}
static int partno_from_devname(char *s)
{
int pno;
size_t sz;
char *end, *p;
sz = rtrim_whitespace((unsigned char *)s);
p = s + sz - 1;
while (p > s && isdigit(*(p - 1)))
p--;
errno = 0;
pno = strtol(p, &end, 10);
if (errno || !end || p == end)
return -1;
return pno - 1;
}
/* dump format
* <device>: start=<num>, size=<num>, type=<string>, ...
*/
static int parse_script_line(struct fdisk_script *dp, char *s)
{
char *p, *x;
struct fdisk_partition *pa;
int rc = 0;
uint64_t num;
int pno;
assert(dp);
assert(s);
DBG(SCRIPT, ul_debugobj(dp, " parse script line: '%s'", s));
pa = fdisk_new_partition();
if (!pa)
return -ENOMEM;
fdisk_partition_start_follow_default(pa, 1);
fdisk_partition_end_follow_default(pa, 1);
fdisk_partition_partno_follow_default(pa, 1);
/* set partno */
p = strchr(s, ':');
x = strchr(s, '=');
if (p && (!x || p < x)) {
*p = '\0';
p++;
pno = partno_from_devname(s);
if (pno >= 0) {
fdisk_partition_partno_follow_default(pa, 0);
fdisk_partition_set_partno(pa, pno);
}
} else
p = s;
while (rc == 0 && p && *p) {
DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
p = (char *) skip_blank(p);
if (!strncasecmp(p, "start=", 6)) {
p += 6;
rc = next_number(&p, &num, NULL);
if (!rc) {
fdisk_partition_set_start(pa, num);
fdisk_partition_start_follow_default(pa, 0);
}
} else if (!strncasecmp(p, "size=", 5)) {
int pow = 0;
p += 5;
rc = next_number(&p, &num, &pow);
if (!rc) {
if (pow) /* specified as <num><suffix> */
num /= dp->cxt->sector_size;
else /* specified as number of sectors */
fdisk_partition_size_explicit(pa, 1);
fdisk_partition_set_size(pa, num);
fdisk_partition_end_follow_default(pa, 0);
}
} else if (!strncasecmp(p, "bootable", 8)) {
char *tk = next_token(&p);
if (strcmp(tk, "bootable") == 0)
pa->boot = 1;
else
rc = -EINVAL;
} else if (!strncasecmp(p, "attrs=", 6)) {
p += 6;
rc = next_string(&p, &pa->attrs);
} else if (!strncasecmp(p, "uuid=", 5)) {
p += 5;
rc = next_string(&p, &pa->uuid);
} else if (!strncasecmp(p, "name=", 5)) {
p += 5;
rc = next_string(&p, &pa->name);
} else if (!strncasecmp(p, "type=", 5) ||
!strncasecmp(p, "Id=", 3)) { /* backward compatiility */
char *type;
p += (*p == 'I' ? 3 : 5); /* "Id=" or "type=" */
rc = next_string(&p, &type);
if (rc)
break;
pa->type = fdisk_label_parse_parttype(
script_get_label(dp), type);
free(type);
if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
rc = -EINVAL;
fdisk_unref_parttype(pa->type);
pa->type = NULL;
break;
}
} else {
DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
rc = -EINVAL;
break;
}
}
if (!rc)
rc = fdisk_table_add_partition(dp->table, pa);
if (rc)
DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
fdisk_unref_partition(pa);
return rc;
}
/* original sfdisk supports partition types shortcuts like 'L' = Linux native
*/
static struct fdisk_parttype *translate_type_shortcuts(struct fdisk_script *dp, char *str)
{
struct fdisk_label *lb;
const char *type = NULL;
if (strlen(str) != 1)
return NULL;
lb = script_get_label(dp);
if (!lb)
return NULL;
if (lb->id == FDISK_DISKLABEL_DOS) {
switch (*str) {
case 'L': /* Linux */
type = "83";
break;
case 'S': /* Swap */
type = "82";
break;
case 'E': /* Dos extended */
type = "05";
break;
case 'X': /* Linux extended */
type = "85";
break;
}
} else if (lb->id == FDISK_DISKLABEL_GPT) {
switch (*str) {
case 'L': /* Linux */
type = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
break;
case 'S': /* Swap */
type = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F";
break;
case 'H': /* Home */
type = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915";
break;
}
}
return type ? fdisk_label_parse_parttype(lb, type) : NULL;
}
/* simple format:
* <start>, <size>, <type>, <bootable>, ...
*/
static int parse_commas_line(struct fdisk_script *dp, char *s)
{
int rc = 0;
char *p = s, *str;
struct fdisk_partition *pa;
enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
int item = -1;
assert(dp);
assert(s);
pa = fdisk_new_partition();
if (!pa)
return -ENOMEM;
fdisk_partition_start_follow_default(pa, 1);
fdisk_partition_end_follow_default(pa, 1);
fdisk_partition_partno_follow_default(pa, 1);
while (rc == 0 && p && *p) {
uint64_t num;
char *begin;
p = (char *) skip_blank(p);
item++;
DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
begin = p;
switch (item) {
case ITEM_START:
if (*p == ',' || *p == ';')
fdisk_partition_start_follow_default(pa, 1);
else {
rc = next_number(&p, &num, NULL);
if (!rc)
fdisk_partition_set_start(pa, num);
fdisk_partition_start_follow_default(pa, 0);
}
break;
case ITEM_SIZE:
if (*p == ',' || *p == ';' || *p == '+')
fdisk_partition_end_follow_default(pa, 1);
else {
int pow = 0;
rc = next_number(&p, &num, &pow);
if (!rc) {
if (pow) /* specified as <size><suffix> */
num /= dp->cxt->sector_size;
else /* specified as number of sectors */
fdisk_partition_size_explicit(pa, 1);
fdisk_partition_set_size(pa, num);
}
fdisk_partition_end_follow_default(pa, 0);
}
break;
case ITEM_TYPE:
if (*p == ',' || *p == ';')
break; /* use default type */
rc = next_string(&p, &str);
if (rc)
break;
pa->type = translate_type_shortcuts(dp, str);
if (!pa->type)
pa->type = fdisk_label_parse_parttype(
script_get_label(dp), str);
free(str);
if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
rc = -EINVAL;
fdisk_unref_parttype(pa->type);
pa->type = NULL;
break;
}
break;
case ITEM_BOOTABLE:
if (*p == ',' || *p == ';')
break;
else {
char *tk = next_token(&p);
if (tk && *tk == '*' && *(tk + 1) == '\0')
pa->boot = 1;
else if (tk && *tk == '-' && *(tk + 1) == '\0')
pa->boot = 0;
else
rc = -EINVAL;
}
break;
default:
break;
}
if (begin == p)
p++;
}
if (!rc)
rc = fdisk_table_add_partition(dp->table, pa);
if (rc)
DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
fdisk_unref_partition(pa);
return rc;
}
/* modifies @s ! */
int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
{
int rc = 0;
assert(dp);
assert(s);
DBG(SCRIPT, ul_debugobj(dp, " parsing buffer"));
s = (char *) skip_blank(s);
if (!s || !*s)
return 0; /* nothing baby, ignore */
if (!dp->table) {
dp->table = fdisk_new_table();
if (!dp->table)
return -ENOMEM;
}
/* parse header lines only if no partition specified yet */
if (fdisk_table_is_empty(dp->table) && is_header_line(s))
rc = parse_header_line(dp, s);
/* parse script format */
else if (strchr(s, '='))
rc = parse_script_line(dp, s);
/* parse simple <value>, ... format */
else
rc = parse_commas_line(dp, s);
if (rc)
DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
dp->nlines, rc));
return rc;
}
/**
* fdisk_script_read_line:
* @dp: script
* @f: file
* @buf: buffer to store one line of the file
* @bufsz: buffer size
*
* Reads next line into dump.
*
* Returns: 0 on success, <0 on error, 1 when nothing to read.
*/
int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
{
char *s;
assert(dp);
assert(f);
DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
/* read the next non-blank non-comment line */
do {
if (fgets(buf, bufsz, f) == NULL)
return 1;
dp->nlines++;
s = strchr(buf, '\n');
if (!s) {
/* Missing final newline? Otherwise an extremely */
/* long line - assume file was corrupted */
if (feof(f)) {
DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
s = strchr(buf, '\0');
} else {
DBG(SCRIPT, ul_debugobj(dp,
"%zu: missing newline at line", dp->nlines));
return -EINVAL;
}
}
*s = '\0';
if (--s >= buf && *s == '\r')
*s = '\0';
s = (char *) skip_blank(buf);
} while (*s == '\0' || *s == '#');
return fdisk_script_read_buffer(dp, s);
}
/**
* fdisk_script_read_file:
* @dp: script
* @f: input file
*
* Reads file @f into script @dp.
*
* Returns: 0 on success, <0 on error.
*/
int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
{
char buf[BUFSIZ];
int rc;
assert(dp);
assert(f);
DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
while (!feof(f)) {
rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
if (rc)
break;
}
if (rc == 1)
rc = 0; /* end of file */
DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
return rc;
}
/**
* fdisk_set_script:
* @cxt: context
* @dp: script (or NULL to remove previous reference)
*
* Sets reference to the @dp script. The script headers might be used by label
* drivers to overwrite built-in defaults (for example disk label Id) and label
* driver might optimize the default semantic to be more usable for scripts
* (for example to not ask for primary/logical/extended partition type).
*
* Note that script also contains reference to the fdisk context (see
* fdisk_new_script()). This context may be completely independent on
* context used for fdisk_set_script().
*
* Returns: <0 on error, 0 on success.
*/
int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
{
assert(cxt);
/* unref old */
if (cxt->script)
fdisk_unref_script(cxt->script);
/* ref new */
cxt->script = dp;
if (cxt->script) {
DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
fdisk_ref_script(cxt->script);
}
return 0;
}
/**
* fdisk_get_script:
* @cxt: context
*
* Returns: the current script or NULL.
*/
struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
{
assert(cxt);
return cxt->script;
}
/**
* fdisk_apply_script_headers:
* @cxt: context
* @dp: script
*
* Associte context @cxt with script @dp and creates a new empty disklabel.
*
* Returns: 0 on success, <0 on error.
*/
int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
{
const char *name;
assert(cxt);
assert(dp);
DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
fdisk_set_script(cxt, dp);
/* create empty label */
name = fdisk_script_get_header(dp, "label");
if (!name)
return -EINVAL;
return fdisk_create_disklabel(cxt, name);
}
/**
* fdisk_apply_script:
* @cxt: context
* @dp: script
*
* This function creates a new disklabel and partition within context @cxt. You
* have to call fdisk_write_disklabel() to apply changes to the device.
*
* Returns: 0 on error, <0 on error.
*/
int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
{
int rc;
struct fdisk_script *old;
assert(dp);
assert(cxt);
DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
old = fdisk_get_script(cxt);
/* create empty disk label */
rc = fdisk_apply_script_headers(cxt, dp);
/* create partitions */
if (!rc && dp->table)
rc = fdisk_apply_table(cxt, dp->table);
fdisk_set_script(cxt, old);
DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
return rc;
}
#ifdef TEST_PROGRAM
int test_dump(struct fdisk_test *ts, int argc, char *argv[])
{
char *devname = argv[1];
struct fdisk_context *cxt;
struct fdisk_script *dp;
cxt = fdisk_new_context();
fdisk_assign_device(cxt, devname, 1);
dp = fdisk_new_script(cxt);
fdisk_script_read_context(dp, NULL);
fdisk_script_write_file(dp, stdout);
fdisk_unref_script(dp);
fdisk_unref_context(cxt);
return 0;
}
int test_read(struct fdisk_test *ts, int argc, char *argv[])
{
char *filename = argv[1];
struct fdisk_script *dp;
struct fdisk_context *cxt;
FILE *f;
if (!(f = fopen(filename, "r")))
err(EXIT_FAILURE, "%s: cannot open", filename);
cxt = fdisk_new_context();
dp = fdisk_new_script(cxt);
fdisk_script_read_file(dp, f);
fclose(f);
fdisk_script_write_file(dp, stdout);
fdisk_unref_script(dp);
fdisk_unref_context(cxt);
return 0;
}
int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
{
char buf[BUFSIZ];
struct fdisk_script *dp;
struct fdisk_context *cxt;
int rc = 0;
cxt = fdisk_new_context();
dp = fdisk_new_script(cxt);
fdisk_script_set_header(dp, "label", "dos");
printf("<start>, <size>, <type>, <bootable: *|->\n");
do {
struct fdisk_partition *pa;
size_t n = fdisk_table_get_nents(dp->table);
printf(" #%zu :\n", n + 1);
rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
if (rc == 0) {
pa = fdisk_table_get_partition(dp->table, n);
printf(" #%zu %12ju %12ju\n", n + 1,
fdisk_partition_get_start(pa),
fdisk_partition_get_size(pa));
}
} while (rc == 0);
if (!rc)
fdisk_script_write_file(dp, stdout);
fdisk_unref_script(dp);
fdisk_unref_context(cxt);
return rc;
}
int test_apply(struct fdisk_test *ts, int argc, char *argv[])
{
char *devname = argv[1], *scriptname = argv[2];
struct fdisk_context *cxt;
struct fdisk_script *dp = NULL;
struct fdisk_table *tb = NULL;
struct fdisk_iter *itr = NULL;
struct fdisk_partition *pa = NULL;
int rc;
cxt = fdisk_new_context();
fdisk_assign_device(cxt, devname, 0);
dp = fdisk_new_script_from_file(cxt, scriptname);
if (!dp)
return -errno;
rc = fdisk_apply_script(cxt, dp);
if (rc)
goto done;
fdisk_unref_script(dp);
/* list result */
fdisk_list_disklabel(cxt);
fdisk_get_partitions(cxt, &tb);
itr = fdisk_new_iter(FDISK_ITER_FORWARD);
while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
printf(" #%zu %12ju %12ju\n", fdisk_partition_get_partno(pa),
fdisk_partition_get_start(pa),
fdisk_partition_get_size(pa));
}
done:
fdisk_free_iter(itr);
fdisk_unref_table(tb);
/*fdisk_write_disklabel(cxt);*/
fdisk_unref_context(cxt);
return 0;
}
int main(int argc, char *argv[])
{
struct fdisk_test tss[] = {
{ "--dump", test_dump, "<device> dump PT as script" },
{ "--read", test_read, "<file> read PT script from file" },
{ "--apply", test_apply, "<device> <file> try apply script from file to device" },
{ "--stdin", test_stdin, " read input like sfdisk" },
{ NULL }
};
return fdisk_run_test(tss, argc, argv);
}
#endif