diff options
Diffstat (limited to 'lib/tar')
-rw-r--r-- | lib/tar/checksum.c | 26 | ||||
-rw-r--r-- | lib/tar/cleanup.c | 21 | ||||
-rw-r--r-- | lib/tar/internal.h | 55 | ||||
-rw-r--r-- | lib/tar/number.c | 94 | ||||
-rw-r--r-- | lib/tar/read_header.c | 366 | ||||
-rw-r--r-- | lib/tar/read_sparse_map.c | 47 | ||||
-rw-r--r-- | lib/tar/read_sparse_map_old.c | 89 | ||||
-rw-r--r-- | lib/tar/write_header.c | 30 |
8 files changed, 371 insertions, 357 deletions
diff --git a/lib/tar/checksum.c b/lib/tar/checksum.c new file mode 100644 index 0000000..925f942 --- /dev/null +++ b/lib/tar/checksum.c @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "internal.h" + +void update_checksum(tar_header_t *hdr) +{ + unsigned int chksum = 0; + size_t i; + + memset(hdr->chksum, ' ', sizeof(hdr->chksum)); + + for (i = 0; i < sizeof(*hdr); ++i) + chksum += ((unsigned char *)hdr)[i]; + + write_octal(hdr->chksum, chksum, 6); + hdr->chksum[6] = '\0'; + hdr->chksum[7] = ' '; +} + +bool is_checksum_valid(const tar_header_t *hdr) +{ + tar_header_t copy; + + memcpy(©, hdr, sizeof(*hdr)); + update_checksum(©); + return memcmp(hdr, ©, sizeof(*hdr)) == 0; +} diff --git a/lib/tar/cleanup.c b/lib/tar/cleanup.c new file mode 100644 index 0000000..c4d1734 --- /dev/null +++ b/lib/tar/cleanup.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "internal.h" + +void free_sparse_list(sparse_map_t *sparse) +{ + sparse_map_t *old; + + while (sparse != NULL) { + old = sparse; + sparse = sparse->next; + free(old); + } +} + +void clear_header(tar_header_decoded_t *hdr) +{ + free_sparse_list(hdr->sparse); + free(hdr->name); + free(hdr->link_target); + memset(hdr, 0, sizeof(*hdr)); +} diff --git a/lib/tar/internal.h b/lib/tar/internal.h new file mode 100644 index 0000000..6596987 --- /dev/null +++ b/lib/tar/internal.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "util.h" +#include "tar.h" + +#include <sys/sysmacros.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdio.h> + +enum { + PAX_SIZE = 0x001, + PAX_UID = 0x002, + PAX_GID = 0x004, + PAX_DEV_MAJ = 0x008, + PAX_DEV_MIN = 0x010, + PAX_NAME = 0x020, + PAX_SLINK_TARGET = 0x040, + PAX_ATIME = 0x080, + PAX_MTIME = 0x100, + PAX_CTIME = 0x200, + PAX_SPARSE_SIZE = 0x400, +}; + +enum { + ETV_UNKNOWN = 0, + ETV_V7_UNIX, + ETV_PRE_POSIX, + ETV_POSIX, +}; + +int read_octal(const char *str, int digits, uint64_t *out); + +int read_binary(const char *str, int digits, uint64_t *out); + +int read_number(const char *str, int digits, uint64_t *out); + +void write_octal(char *dst, unsigned int value, int digits); + +int pax_read_decimal(const char *str, uint64_t *out); + +void update_checksum(tar_header_t *hdr); + +bool is_checksum_valid(const tar_header_t *hdr); + +sparse_map_t *read_sparse_map(const char *line); + +sparse_map_t *read_gnu_old_sparse(int fd, tar_header_t *hdr); + +void free_sparse_list(sparse_map_t *sparse); + +#endif /* INTERNAL_H */ diff --git a/lib/tar/number.c b/lib/tar/number.c new file mode 100644 index 0000000..e85866d --- /dev/null +++ b/lib/tar/number.c @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "internal.h" + +int read_octal(const char *str, int digits, uint64_t *out) +{ + uint64_t result = 0; + + while (digits > 0 && isspace(*str)) { + ++str; + --digits; + } + + while (digits > 0 && *str >= '0' && *str <= '7') { + if (result > 0x1FFFFFFFFFFFFFFFUL) { + fputs("numeric overflow parsing tar header\n", stderr); + return -1; + } + + result = (result << 3) | (*(str++) - '0'); + --digits; + } + + *out = result; + return 0; +} + +int read_binary(const char *str, int digits, uint64_t *out) +{ + uint64_t x, ov, result = 0; + bool first = true; + + while (digits > 0) { + x = *((const unsigned char *)str++); + --digits; + + if (first) { + first = false; + if (x == 0xFF) { + result = 0xFFFFFFFFFFFFFFFFUL; + } else { + x &= 0x7F; + result = 0; + if (digits > 7 && x != 0) + goto fail_ov; + } + } + + ov = (result >> 56) & 0xFF; + + if (ov != 0 && ov != 0xFF) + goto fail_ov; + + result = (result << 8) | x; + } + + *out = result; + return 0; +fail_ov: + fputs("numeric overflow parsing tar header\n", stderr); + return -1; +} + +int read_number(const char *str, int digits, uint64_t *out) +{ + if (*((unsigned char *)str) & 0x80) + return read_binary(str, digits, out); + + return read_octal(str, digits, out); +} + +int pax_read_decimal(const char *str, uint64_t *out) +{ + uint64_t result = 0; + + while (*str >= '0' && *str <= '9') { + if (result > 0xFFFFFFFFFFFFFFFFUL / 10) { + fputs("numeric overflow parsing pax header\n", stderr); + return -1; + } + + result = (result * 10) + (*(str++) - '0'); + } + + *out = result; + return 0; +} + +void write_octal(char *dst, unsigned int value, int digits) +{ + char temp[64]; + + sprintf(temp, "%0*o ", digits, value); + memcpy(dst, temp, strlen(temp)); +} diff --git a/lib/tar/read_header.c b/lib/tar/read_header.c index de6f0cd..067b1df 100644 --- a/lib/tar/read_header.c +++ b/lib/tar/read_header.c @@ -1,104 +1,5 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ -#include "util.h" -#include "tar.h" - -#include <sys/sysmacros.h> -#include <string.h> -#include <stdlib.h> -#include <ctype.h> -#include <stdio.h> - -enum { - PAX_SIZE = 0x001, - PAX_UID = 0x002, - PAX_GID = 0x004, - PAX_DEV_MAJ = 0x008, - PAX_DEV_MIN = 0x010, - PAX_NAME = 0x020, - PAX_SLINK_TARGET = 0x040, - PAX_ATIME = 0x080, - PAX_MTIME = 0x100, - PAX_CTIME = 0x200, - PAX_SPARSE_SIZE = 0x400, -}; - -static void free_sparse_list(sparse_map_t *sparse) -{ - sparse_map_t *old; - - while (sparse != NULL) { - old = sparse; - sparse = sparse->next; - free(old); - } -} - -static int read_octal(const char *str, int digits, uint64_t *out) -{ - uint64_t result = 0; - - while (digits > 0 && isspace(*str)) { - ++str; - --digits; - } - - while (digits > 0 && *str >= '0' && *str <= '7') { - if (result > 0x1FFFFFFFFFFFFFFFUL) { - fputs("numeric overflow parsing tar header\n", stderr); - return -1; - } - - result = (result << 3) | (*(str++) - '0'); - --digits; - } - - *out = result; - return 0; -} - -static int read_binary(const char *str, int digits, uint64_t *out) -{ - uint64_t x, ov, result; - bool first = true; - - while (digits > 0) { - x = *((const unsigned char *)str++); - --digits; - - if (first) { - first = false; - if (x == 0xFF) { - result = 0xFFFFFFFFFFFFFFFFUL; - } else { - x &= 0x7F; - result = 0; - if (digits > 7 && x != 0) - goto fail_ov; - } - } - - ov = (result >> 56) & 0xFF; - - if (ov != 0 && ov != 0xFF) - goto fail_ov; - - result = (result << 8) | x; - } - - *out = result; - return 0; -fail_ov: - fputs("numeric overflow parsing tar header\n", stderr); - return -1; -} - -static int read_number(const char *str, int digits, uint64_t *out) -{ - if (*((unsigned char *)str) & 0x80) - return read_binary(str, digits, out); - - return read_octal(str, digits, out); -} +#include "internal.h" static bool is_zero_block(const tar_header_t *hdr) { @@ -107,26 +8,7 @@ static bool is_zero_block(const tar_header_t *hdr) return ptr[0] == '\0' && memcmp(ptr, ptr + 1, sizeof(*hdr) - 1) == 0; } -static bool is_checksum_valid(const tar_header_t *hdr) -{ - unsigned int chksum = 0; - tar_header_t copy; - uint64_t ref; - size_t i; - - if (read_octal(hdr->chksum, sizeof(hdr->chksum), &ref)) - return false; - - memcpy(©, hdr, sizeof(*hdr)); - memset(copy.chksum, ' ', sizeof(copy.chksum)); - - for (i = 0; i < sizeof(copy); ++i) - chksum += ((unsigned char *)©)[i]; - - return chksum == ref; -} - -static E_TAR_VERSION check_version(const tar_header_t *hdr) +static int check_version(const tar_header_t *hdr) { char buffer[sizeof(hdr->magic) + sizeof(hdr->version)]; @@ -146,65 +28,35 @@ static E_TAR_VERSION check_version(const tar_header_t *hdr) return ETV_UNKNOWN; } -static int pax_read_decimal(const char *str, uint64_t *out) -{ - uint64_t result = 0; - - while (*str >= '0' && *str <= '9') { - if (result > 0xFFFFFFFFFFFFFFFFUL / 10) { - fputs("numeric overflow parsing pax header\n", stderr); - return -1; - } - - result = (result * 10) + (*(str++) - '0'); - } - - *out = result; - return 0; -} - -static sparse_map_t *read_sparse_map(const char *line) +static char *record_to_memory(int fd, uint64_t size) { - sparse_map_t *last = NULL, *list = NULL, *ent = NULL; - - do { - ent = calloc(1, sizeof(*ent)); - if (ent == NULL) - goto fail_errno; - - if (pax_read_decimal(line, &ent->offset)) - goto fail_format; - - while (isdigit(*line)) - ++line; - - if (*(line++) != ',') - goto fail_format; + char *buffer = malloc(size + 1); + ssize_t ret; - if (pax_read_decimal(line, &ent->count)) - goto fail_format; + if (buffer == NULL) + goto fail_errno; - while (isdigit(*line)) - ++line; + ret = read_retry(fd, buffer, size); + if (ret == 0) + goto fail_eof; + if (ret < 0) + goto fail_errno; + if ((uint64_t)ret < size) + goto fail_eof; - if (last == NULL) { - list = last = ent; - } else { - last->next = ent; - last = ent; - } - } while (*(line++) == ','); + if (skip_padding(fd, size)) + goto fail; - return list; + buffer[size] = '\0'; + return buffer; fail_errno: - perror("parsing GNU pax sparse file record"); + perror("reading tar record"); goto fail; -fail_format: - fputs("malformed GNU pax sparse file record\n", stderr); +fail_eof: + fputs("reading tar record: unexpected end of file\n", stderr); goto fail; fail: - free_sparse_list(list); - free(ent); + free(buffer); return NULL; } @@ -214,23 +66,11 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, sparse_map_t *sparse_last = NULL, *sparse; uint64_t field, offset = 0, num_bytes = 0; char *buffer, *line; - ssize_t ret; uint64_t i; - buffer = malloc(entsize + 1); + buffer = record_to_memory(fd, entsize); if (buffer == NULL) - goto fail_errno; - - ret = read_retry(fd, buffer, entsize); - if (ret < 0) - goto fail_errno; - if ((size_t)ret < entsize) - goto fail_eof; - - if (skip_padding(fd, entsize)) - goto fail; - - buffer[entsize] = '\0'; + return -1; for (i = 0; i < entsize; ++i) { while (i < entsize && isspace(buffer[i])) @@ -251,12 +91,12 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, if (!strncmp(line, "uid=", 4)) { if (pax_read_decimal(line + 4, &field)) - return -1; + goto fail; out->sb.st_uid = field; *set_by_pax |= PAX_UID; } else if (!strncmp(line, "gid=", 4)) { if (pax_read_decimal(line + 4, &field)) - return -1; + goto fail; out->sb.st_gid = field; *set_by_pax |= PAX_GID; } else if (!strncmp(line, "path=", 5)) { @@ -267,7 +107,7 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, *set_by_pax |= PAX_NAME; } else if (!strncmp(line, "size=", 5)) { if (pax_read_decimal(line + 5, &out->record_size)) - return -1; + goto fail; *set_by_pax |= PAX_SIZE; } else if (!strncmp(line, "linkpath=", 9)) { free(out->link_target); @@ -278,33 +118,33 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, } else if (!strncmp(line, "atime=", 6)) { if (line[6] == '-') { if (pax_read_decimal(line + 7, &field)) - return -1; + goto fail; out->sb.st_atime = -((int64_t)field); } else { if (pax_read_decimal(line + 6, &field)) - return -1; + goto fail; out->sb.st_atime = field; } *set_by_pax |= PAX_ATIME; } else if (!strncmp(line, "mtime=", 6)) { if (line[6] == '-') { if (pax_read_decimal(line + 7, &field)) - return -1; + goto fail; out->sb.st_mtime = -((int64_t)field); } else { if (pax_read_decimal(line + 6, &field)) - return -1; + goto fail; out->sb.st_mtime = field; } *set_by_pax |= PAX_MTIME; } else if (!strncmp(line, "ctime=", 6)) { if (line[6] == '-') { if (pax_read_decimal(line + 7, &field)) - return -1; + goto fail; out->sb.st_ctime = -((int64_t)field); } else { if (pax_read_decimal(line + 6, &field)) - return -1; + goto fail; out->sb.st_ctime = field; } *set_by_pax |= PAX_CTIME; @@ -323,14 +163,14 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, goto fail; } else if (!strncmp(line, "GNU.sparse.size=", 16)) { if (pax_read_decimal(line + 16, &out->actual_size)) - return -1; + goto fail; *set_by_pax |= PAX_SPARSE_SIZE; } else if (!strncmp(line, "GNU.sparse.offset=", 18)) { if (pax_read_decimal(line + 18, &offset)) - return -1; + goto fail; } else if (!strncmp(line, "GNU.sparse.numbytes=", 20)) { if (pax_read_decimal(line + 20, &num_bytes)) - return -1; + goto fail; sparse = calloc(1, sizeof(*sparse)); if (sparse == NULL) goto fail_errno; @@ -351,16 +191,13 @@ static int read_pax_header(int fd, uint64_t entsize, unsigned int *set_by_pax, fail_errno: perror("reading pax header"); goto fail; -fail_eof: - fputs("reading pax header: unexpected end of file\n", stderr); - goto fail; fail: free(buffer); return -1; } static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax, - tar_header_decoded_t *out, E_TAR_VERSION version) + tar_header_decoded_t *out, int version) { uint64_t field; size_t count; @@ -520,132 +357,13 @@ static int decode_header(const tar_header_t *hdr, unsigned int set_by_pax, return 0; } -static char *record_to_memory(int fd, uint64_t size) -{ - char *buffer = malloc(size + 1); - ssize_t ret; - - if (buffer == NULL) - goto fail_errno; - - ret = read_retry(fd, buffer, size); - if (ret == 0) - goto fail_eof; - if (ret < 0) - goto fail_errno; - if ((uint64_t)ret < size) - goto fail_eof; - - if (skip_padding(fd, size)) - goto fail; - - return buffer; -fail_errno: - perror("reading tar record"); - goto fail; -fail_eof: - fputs("reading tar record: unexpected end of file\n", stderr); - goto fail; -fail: - free(buffer); - return NULL; -} - -static sparse_map_t *read_gnu_old_sparse(int fd, tar_header_t *hdr) -{ - sparse_map_t *list = NULL, *end = NULL, *node; - gnu_sparse_t sph; - uint64_t off, sz; - ssize_t ret; - int i; - - for (i = 0; i < 4; ++i) { - if (!isdigit(hdr->tail.gnu.sparse[i].offset[0])) - break; - if (!isdigit(hdr->tail.gnu.sparse[i].numbytes[0])) - break; - - if (read_octal(hdr->tail.gnu.sparse[i].offset, - sizeof(hdr->tail.gnu.sparse[i].offset), &off)) - goto fail; - if (read_octal(hdr->tail.gnu.sparse[i].numbytes, - sizeof(hdr->tail.gnu.sparse[i].numbytes), &sz)) - goto fail; - - node = calloc(1, sizeof(*node)); - if (node == NULL) - goto fail_errno; - - node->offset = off; - node->count = sz; - - if (list == NULL) { - list = end = node; - } else { - end->next = node; - end = node; - } - } - - if (hdr->tail.gnu.isextended == 0) - return list; - - do { - ret = read_retry(fd, &sph, sizeof(sph)); - if (ret < 0) - goto fail_errno; - if ((size_t)ret < sizeof(sph)) - goto fail_eof; - - for (i = 0; i < 21; ++i) { - if (!isdigit(sph.sparse[i].offset[0])) - break; - if (!isdigit(sph.sparse[i].numbytes[0])) - break; - - if (read_octal(sph.sparse[i].offset, - sizeof(sph.sparse[i].offset), &off)) - goto fail; - if (read_octal(sph.sparse[i].numbytes, - sizeof(sph.sparse[i].numbytes), &sz)) - goto fail; - - node = calloc(1, sizeof(*node)); - if (node == NULL) - goto fail_errno; - - node->offset = off; - node->count = sz; - - if (list == NULL) { - list = end = node; - } else { - end->next = node; - end = node; - } - } - } while (sph.isextended != 0); - - return list; -fail_eof: - fputs("parsing GNU sparse header: unexpected end of file", stderr); - goto fail; -fail_errno: - perror("parsing GNU sparse header"); - goto fail; -fail: - free_sparse_list(list); - return NULL; -} - int read_header(int fd, tar_header_decoded_t *out) { unsigned int set_by_pax = 0; bool prev_was_zero = false; - E_TAR_VERSION version; uint64_t pax_size; tar_header_t hdr; - int ret; + int ret, version; memset(out, 0, sizeof(*out)); @@ -744,11 +462,3 @@ fail: clear_header(out); return -1; } - -void clear_header(tar_header_decoded_t *hdr) -{ - free_sparse_list(hdr->sparse); - free(hdr->name); - free(hdr->link_target); - memset(hdr, 0, sizeof(*hdr)); -} diff --git a/lib/tar/read_sparse_map.c b/lib/tar/read_sparse_map.c new file mode 100644 index 0000000..77876f5 --- /dev/null +++ b/lib/tar/read_sparse_map.c @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "internal.h" + +sparse_map_t *read_sparse_map(const char *line) +{ + sparse_map_t *last = NULL, *list = NULL, *ent = NULL; + + do { + ent = calloc(1, sizeof(*ent)); + if (ent == NULL) + goto fail_errno; + + if (pax_read_decimal(line, &ent->offset)) + goto fail_format; + + while (isdigit(*line)) + ++line; + + if (*(line++) != ',') + goto fail_format; + + if (pax_read_decimal(line, &ent->count)) + goto fail_format; + + while (isdigit(*line)) + ++line; + + if (last == NULL) { + list = last = ent; + } else { + last->next = ent; + last = ent; + } + } while (*(line++) == ','); + + return list; +fail_errno: + perror("parsing GNU pax sparse file record"); + goto fail; +fail_format: + fputs("malformed GNU pax sparse file record\n", stderr); + goto fail; +fail: + free_sparse_list(list); + free(ent); + return NULL; +} diff --git a/lib/tar/read_sparse_map_old.c b/lib/tar/read_sparse_map_old.c new file mode 100644 index 0000000..c0b2d0d --- /dev/null +++ b/lib/tar/read_sparse_map_old.c @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "internal.h" + +sparse_map_t *read_gnu_old_sparse(int fd, tar_header_t *hdr) +{ + sparse_map_t *list = NULL, *end = NULL, *node; + gnu_sparse_t sph; + uint64_t off, sz; + ssize_t ret; + int i; + + for (i = 0; i < 4; ++i) { + if (!isdigit(hdr->tail.gnu.sparse[i].offset[0])) + break; + if (!isdigit(hdr->tail.gnu.sparse[i].numbytes[0])) + break; + + if (read_octal(hdr->tail.gnu.sparse[i].offset, + sizeof(hdr->tail.gnu.sparse[i].offset), &off)) + goto fail; + if (read_octal(hdr->tail.gnu.sparse[i].numbytes, + sizeof(hdr->tail.gnu.sparse[i].numbytes), &sz)) + goto fail; + + node = calloc(1, sizeof(*node)); + if (node == NULL) + goto fail_errno; + + node->offset = off; + node->count = sz; + + if (list == NULL) { + list = end = node; + } else { + end->next = node; + end = node; + } + } + + if (hdr->tail.gnu.isextended == 0) + return list; + + do { + ret = read_retry(fd, &sph, sizeof(sph)); + if (ret < 0) + goto fail_errno; + if ((size_t)ret < sizeof(sph)) + goto fail_eof; + + for (i = 0; i < 21; ++i) { + if (!isdigit(sph.sparse[i].offset[0])) + break; + if (!isdigit(sph.sparse[i].numbytes[0])) + break; + + if (read_octal(sph.sparse[i].offset, + sizeof(sph.sparse[i].offset), &off)) + goto fail; + if (read_octal(sph.sparse[i].numbytes, + sizeof(sph.sparse[i].numbytes), &sz)) + goto fail; + + node = calloc(1, sizeof(*node)); + if (node == NULL) + goto fail_errno; + + node->offset = off; + node->count = sz; + + if (list == NULL) { + list = end = node; + } else { + end->next = node; + end = node; + } + } + } while (sph.isextended != 0); + + return list; +fail_eof: + fputs("parsing GNU sparse header: unexpected end of file", stderr); + goto fail; +fail_errno: + perror("parsing GNU sparse header"); + goto fail; +fail: + free_sparse_list(list); + return NULL; +} diff --git a/lib/tar/write_header.c b/lib/tar/write_header.c index 0670523..b84324b 100644 --- a/lib/tar/write_header.c +++ b/lib/tar/write_header.c @@ -1,22 +1,9 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ -#include "util.h" -#include "tar.h" - -#include <sys/sysmacros.h> -#include <string.h> -#include <stdio.h> +#include "internal.h" static unsigned long pax_hdr_counter = 0; static char buffer[4096]; -static void write_octal(char *dst, unsigned int value, int digits) -{ - char temp[64]; - - sprintf(temp, "%0*o ", digits, value); - memcpy(dst, temp, strlen(temp)); -} - static int name_to_tar_header(tar_header_t *hdr, const char *path) { size_t len = strlen(path); @@ -80,21 +67,6 @@ static void init_header(tar_header_t *hdr, const struct stat *sb, sprintf(hdr->gname, "%u", sb->st_gid); } -static void update_checksum(tar_header_t *hdr) -{ - unsigned int chksum = 0; - size_t i; - - memset(hdr->chksum, ' ', sizeof(hdr->chksum)); - - for (i = 0; i < sizeof(*hdr); ++i) - chksum += ((unsigned char *)hdr)[i]; - - write_octal(hdr->chksum, chksum, 6); - hdr->chksum[6] = '\0'; - hdr->chksum[7] = ' '; -} - static bool need_pax_header(const struct stat *sb, const char *name) { tar_header_t tmp; |