diff --git a/src/error.h b/src/error.h index 8c0fda3..0410e3b 100644 --- a/src/error.h +++ b/src/error.h @@ -48,6 +48,8 @@ /* image read errors */ #define ISO_WRONG_PVD -300 #define ISO_WRONG_RR -301 +#define ISO_UNSUPPORTED_RR -302 +#define ISO_WRONG_ECMA119 -303 #endif /*LIBISO_ERROR_H_*/ diff --git a/src/rockridge.h b/src/rockridge.h index 4270ff3..f16f1fc 100644 --- a/src/rockridge.h +++ b/src/rockridge.h @@ -221,4 +221,48 @@ int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue); */ void susp_iter_free(SuspIterator *iter); + +/** + * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st); + +/** + * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6) + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st); + +/** + * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to + * the given name. You can pass a pointer to NULL as name. + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont); + +/** + * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues. + * + * @param cont + * 0 not continue, 1 continue, 2 continue component + * @return + * 1 on success, < 0 on error + */ +int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont); + +/** + * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st); + #endif /* LIBISO_ROCKRIDGE_H */ diff --git a/src/rockridge_read.c b/src/rockridge_read.c index c0c6d70..957e2e6 100644 --- a/src/rockridge_read.c +++ b/src/rockridge_read.c @@ -19,6 +19,7 @@ #include #include +#include struct susp_iterator { @@ -138,3 +139,276 @@ void susp_iter_free(SuspIterator *iter) free(iter->buffer); free(iter); } + +/** + * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st) +{ + if (px == NULL || st == NULL) { + return ISO_NULL_POINTER; + } + if (px->sig[0] != 'P' || px->sig[1] != 'X') { + return ISO_WRONG_ARG_VALUE; + } + + if (px->len_sue[0] != 44 || px->len_sue[0] != 36) { + return ISO_WRONG_RR; + } + + st->st_mode = iso_read_bb(px->data.PX.mode, 4, NULL); + st->st_nlink = iso_read_bb(px->data.PX.links, 4, NULL); + st->st_uid = iso_read_bb(px->data.PX.uid, 4, NULL); + st->st_gid = iso_read_bb(px->data.PX.gid, 4, NULL); + if (px->len_sue[0] == 44) { + /* this corresponds to RRIP 1.12, so we have inode serial number */ + st->st_ino = iso_read_bb(px->data.PX.serial, 4, NULL); + } + return ISO_SUCCESS; +} + +/** + * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6) + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st) +{ + time_t time; + int s; + int nts = 0; + + if (tf == NULL || st == NULL) { + return ISO_NULL_POINTER; + } + if (tf->sig[0] != 'T' || tf->sig[1] != 'F') { + return ISO_WRONG_ARG_VALUE; + } + + if (tf->data.TF.flags[0] & (1 << 7)) { + /* long form */ + s = 17; + } else { + s = 7; + } + + /* 1. Creation time */ + if (tf->data.TF.flags[0] & (1 << 0)) { + + /* the creation is the recording time. we ignore this */ + /* TODO maybe it would be good to manage it in ms discs, where + * the recording time could be different than now!! */ + ++nts; + } + + /* 2. modify time */ + if (tf->data.TF.flags[0] & (1 << 1)) { + if (tf->len_sue[0] < 5 + (nts+1) * s) { + /* RR TF entry too short. */ + return ISO_WRONG_RR; + } + if (s == 7) { + time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); + } else { + time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); + } + st->st_mtime = time; + ++nts; + } + + /* 3. access time */ + if (tf->data.TF.flags[0] & (1 << 2)) { + if (tf->len_sue[0] < 5 + (nts+1) * s) { + /* RR TF entry too short. */ + return ISO_WRONG_RR; + } + if (s == 7) { + time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); + } else { + time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); + } + st->st_atime = time; + ++nts; + } + + /* 4. attributes time */ + if (tf->data.TF.flags[0] & (1 << 3)) { + if (tf->len_sue[0] < 5 + (nts+1) * s) { + /* RR TF entry too short. */ + return ISO_WRONG_RR; + } + if (s == 7) { + time = iso_datetime_read_7(&tf->data.TF.t_stamps[nts*7]); + } else { + time = iso_datetime_read_17(&tf->data.TF.t_stamps[nts*17]); + } + st->st_ctime = time; + ++nts; + } + + /* we ignore backup, expire and effect times */ + + return ISO_SUCCESS; +} + +/** + * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to + * the given name. You can pass a pointer to NULL as name. + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont) +{ + if (nm == NULL || name == NULL) { + return ISO_NULL_POINTER; + } + if (nm->sig[0] != 'N' || nm->sig[1] != 'M') { + return ISO_WRONG_ARG_VALUE; + } + + if (nm->len_sue[0] == 5) { + if (nm->data.NM.flags[0] & 0x2) { + /* it is a "." entry */ + if (*name == NULL) { + return ISO_SUCCESS; + } else { + /* we can't have a previous not-NULL name */ + return ISO_WRONG_RR; + } + } + } + + if (nm->len_sue[0] <= 5) { + /* ".." entry is an error, as we will never call it */ + return ISO_WRONG_RR; + } + + /* concatenate the results */ + if (*cont) { + *name = realloc(*name, strlen(*name) + nm->len_sue[0] - 5 + 1); + strncat(*name, (char*)nm->data.NM.name, nm->len_sue[0] - 5); + } else { + *name = strcopy((char*)nm->data.NM.name, nm->len_sue[0] - 5); + } + if (*name == NULL) { + return ISO_MEM_ERROR; + } + + /* and set cond according to the value of CONTINUE flag */ + *cont = nm->data.NM.flags[0] & 0x01; + return ISO_SUCCESS; +} + +/** + * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues. + * + * @param cont + * 0 not continue, 1 continue, 2 continue component + * @return + * 1 on success, < 0 on error + */ +int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont) +{ + int pos; + + if (sl == NULL || dest == NULL) { + return ISO_NULL_POINTER; + } + if (sl->sig[0] != 'S' || sl->sig[1] != 'L') { + return ISO_WRONG_ARG_VALUE; + } + + for (pos = 0; pos + 5 < sl->len_sue[0]; + pos += 2 + sl->data.SL.comps[pos + 1]) { + char *comp; + uint8_t len; + uint8_t flags = sl->data.SL.comps[pos]; + + if (flags & 0x2) { + /* current directory */ + len = 1; + comp = "."; + } else if (flags & 0x4) { + /* parent directory */ + len = 2; + comp = ".."; + } else if (flags & 0x8) { + /* root directory */ + len = 1; + comp = "/"; + } else if (flags & ~0x01) { + /* unsupported flag component */ + return ISO_UNSUPPORTED_RR; + } else { + len = sl->data.SL.comps[pos + 1]; + comp = (char*)&sl->data.SL.comps[pos + 2]; + } + + if (*cont == 1) { + /* new component */ + size_t size = strlen(*dest); + *dest = realloc(*dest, strlen(*dest) + len + 2); + if (*dest == NULL) { + return ISO_MEM_ERROR; + } + /* it is a new compoenent, add the '/' */ + if ((*dest)[size-1] != '/') { + (*dest)[size] = '/'; + (*dest)[size+1] = '\0'; + } + strncat(*dest, comp, len); + } else if (*cont == 2) { + /* the component continues */ + *dest = realloc(*dest, strlen(*dest) + len + 1); + if (*dest == NULL) { + return ISO_MEM_ERROR; + } + /* we don't have to add the '/' */ + strncat(*dest, comp, len); + } else { + *dest = strcopy(comp, len); + } + if (*dest == NULL) { + return ISO_MEM_ERROR; + } + /* do the component continue or not? */ + *cont = (flags & 0x01) ? 2 : 1; + } + + if (*cont == 2) { + /* TODO check that SL flag is set to continute too ?*/ + } else { + *cont = sl->data.SL.flags[0] & 0x1 ? 1 : 0; + } + + return ISO_SUCCESS; +} + +/** + * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2). + * + * @return + * 1 on success, < 0 on error + */ +int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st) +{ + if (pn == NULL || pn == NULL) { + return ISO_NULL_POINTER; + } + if (pn->sig[0] != 'P' || pn->sig[1] != 'N') { + return ISO_WRONG_ARG_VALUE; + } + + if (pn->len_sue[0] != 20) { + return ISO_WRONG_RR; + } + + st->st_rdev = (dev_t)((dev_t)iso_read_bb(pn->data.PN.high, 4, NULL) << 32) + || (dev_t)iso_read_bb(pn->data.PN.low, 4, NULL); + return ISO_SUCCESS; +}