Merge remote-tracking branch 'pali/multisesssion'

* pali/multisesssion:
  libblkid: udf: add support for unclosed sequential Write-Once media
  libblkid: udf: add support for multisession via session_offset hint
  libblkid: iso9660: add support for multisession via session_offset hint
  libblkid: fix blkid_probe_get_sb() to use hint offset calculation
  libblkid: allow to specify offset defined by hint for blkid_probe_get_idmag()
  libblkid: detect session_offset hint for optical discs
  libblkid: do size correction of optical discs also by last written sector
  libblkid: detect CD/DVD discs in packet writing mode
  libblkid: overwrite existing hint
  libblkid: export blkid_probe_reset_hints()
  blkid: add --hint <name>=value
  libblkid: add blkid_probe_{set,get}_hint()
This commit is contained in:
Karel Zak 2020-11-25 14:48:30 +01:00
commit c175692050
8 changed files with 273 additions and 39 deletions

View File

@ -57,7 +57,9 @@ blkid_probe_get_wholedisk_devno
blkid_probe_hide_range
blkid_probe_is_wholedisk
blkid_probe_reset_buffers
blkid_probe_reset_hints
blkid_probe_set_device
blkid_probe_set_hint
blkid_probe_set_sectorsize
blkid_probe_step_back
blkid_reset_probe

View File

@ -255,6 +255,11 @@ extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
extern int blkid_probe_get_fd(blkid_probe pr)
__ul_attribute__((nonnull));
extern int blkid_probe_set_hint(blkid_probe pr, const char *name, uint64_t value)
__ul_attribute__((nonnull));
extern void blkid_probe_reset_hints(blkid_probe pr)
__ul_attribute__((nonnull));
/*
* superblocks probing
*/

View File

@ -147,6 +147,7 @@ struct blkid_idmag
const char *magic; /* magic string */
unsigned int len; /* length of magic */
const char *hoff; /* hint which contains byte offset to kboff */
long kboff; /* kilobyte offset of superblock */
unsigned int sboff; /* byte offset within superblock */
};
@ -183,6 +184,15 @@ struct blkid_bufinfo {
struct list_head bufs; /* list of buffers */
};
/*
* Probing hint
*/
struct blkid_hint {
char *name;
uint64_t value;
struct list_head hints;
};
/*
* Low-level probing control struct
*/
@ -205,6 +215,7 @@ struct blkid_struct_probe
struct blkid_chain *wipe_chain; /* superblock, partition, ... */
struct list_head buffers; /* list of buffers */
struct list_head hints;
struct blkid_chain chains[BLKID_NCHAINS]; /* array of chains */
struct blkid_chain *cur_chain; /* current chain */
@ -417,9 +428,9 @@ extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
__attribute__((nonnull(1)));
/* returns superblock according to 'struct blkid_idmag' */
extern unsigned char *_blkid_probe_get_sb(blkid_probe pr, const struct blkid_idmag *mag, size_t size);
#define blkid_probe_get_sb(_pr, _mag, type) \
((type *) blkid_probe_get_buffer((_pr),\
(_mag)->kboff << 10, sizeof(type)))
((type *) _blkid_probe_get_sb((_pr), _mag, sizeof(type)))
extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
__attribute__((nonnull))
@ -521,6 +532,10 @@ extern int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
extern void blkid_probe_use_wiper(blkid_probe pr, uint64_t off, uint64_t size)
__attribute__((nonnull));
extern int blkid_probe_get_hint(blkid_probe pr, const char *name, uint64_t *value)
__attribute__((nonnull(1,2)))
__attribute__((warn_unused_result));
/* filter bitmap macros */
#define blkid_bmp_wordsize (8 * sizeof(unsigned long))
#define blkid_bmp_idx_bit(item) (1UL << ((item) % blkid_bmp_wordsize))

View File

@ -178,3 +178,8 @@ BLKID_2_31 {
BLKID_2_36 {
blkid_topology_get_dax;
} BLKID_2_31;
BLKID_2_37 {
blkid_probe_set_hint;
blkid_probe_reset_hints;
} BLKID_2_36;

View File

@ -147,6 +147,7 @@ blkid_probe blkid_new_probe(void)
}
INIT_LIST_HEAD(&pr->buffers);
INIT_LIST_HEAD(&pr->values);
INIT_LIST_HEAD(&pr->hints);
return pr;
}
@ -248,6 +249,7 @@ void blkid_free_probe(blkid_probe pr)
close(pr->fd);
blkid_probe_reset_buffers(pr);
blkid_probe_reset_values(pr);
blkid_probe_reset_hints(pr);
blkid_free_probe(pr->disk_probe);
DBG(LOWPROBE, ul_debug("free probe"));
@ -757,6 +759,7 @@ int blkid_probe_hide_range(blkid_probe pr, uint64_t off, uint64_t len)
return rc;
}
static void blkid_probe_reset_values(blkid_probe pr)
{
if (list_empty(&pr->values))
@ -815,12 +818,16 @@ failed:
* readable by read(2). We have to reduce the probing area to avoid unwanted
* I/O errors in probing functions. It seems that unreadable are always last 2
* or 3 CD blocks (CD block size is 2048 bytes, it means 12 in 512-byte
* sectors).
* sectors). Linux kernel reports (CDROM_LAST_WRITTEN) also location of last
* written block, so we will reduce size based on it too.
*/
static void cdrom_size_correction(blkid_probe pr)
static void cdrom_size_correction(blkid_probe pr, uint64_t last_written)
{
uint64_t n, nsectors = pr->size >> 9;
if (last_written && nsectors > ((last_written+1) << 2))
nsectors = (last_written+1) << 2;
for (n = nsectors - 12; n < nsectors; n++) {
if (!is_sector_readable(pr->fd, n))
goto failed;
@ -861,6 +868,9 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
struct stat sb;
uint64_t devsiz = 0;
char *dm_uuid = NULL;
#ifdef CDROM_GET_CAPABILITY
long last_written = 0;
#endif
blkid_reset_probe(pr);
blkid_probe_reset_buffers(pr);
@ -942,19 +952,46 @@ int blkid_probe_set_device(blkid_probe pr, int fd,
else if (S_ISBLK(sb.st_mode) &&
!blkid_probe_is_tiny(pr) &&
!dm_uuid &&
blkid_probe_is_wholedisk(pr) &&
ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0) {
blkid_probe_is_wholedisk(pr)) {
/**
* pktcdvd.ko accepts only these ioctls:
* CDROMEJECT CDROMMULTISESSION CDROMREADTOCENTRY
* CDROM_LAST_WRITTEN CDROM_SEND_PACKET SCSI_IOCTL_SEND_COMMAND
* So CDROM_GET_CAPABILITY cannot be used for detecting pktcdvd
* devices. But CDROM_GET_CAPABILITY and CDROM_DRIVE_STATUS are
* fast so use them for detecting if medium is present. In any
* case use last written block form CDROM_LAST_WRITTEN.
*/
if (ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0) {
# ifdef CDROM_DRIVE_STATUS
switch (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) {
case CDS_TRAY_OPEN:
case CDS_NO_DISC:
errno = ENOMEDIUM;
goto err;
}
switch (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) {
case CDS_TRAY_OPEN:
case CDS_NO_DISC:
errno = ENOMEDIUM;
goto err;
}
# endif
pr->flags |= BLKID_FL_CDROM_DEV;
cdrom_size_correction(pr);
pr->flags |= BLKID_FL_CDROM_DEV;
}
# ifdef CDROM_LAST_WRITTEN
if (ioctl(fd, CDROM_LAST_WRITTEN, &last_written) == 0)
pr->flags |= BLKID_FL_CDROM_DEV;
# endif
if (pr->flags & BLKID_FL_CDROM_DEV) {
cdrom_size_correction(pr, last_written);
# ifdef CDROMMULTISESSION
if (!pr->off && blkid_probe_get_hint(pr, "session_offset", NULL) < 0) {
struct cdrom_multisession multisession = { .addr_format = CDROM_LBA };
if (ioctl(fd, CDROMMULTISESSION, &multisession) == 0 && multisession.xa_flag)
blkid_probe_set_hint(pr, "session_offset", (multisession.addr.lba << 11));
}
# endif
}
}
#endif
free(dm_uuid);
@ -998,6 +1035,16 @@ int blkid_probe_set_dimension(blkid_probe pr, uint64_t off, uint64_t size)
return 0;
}
unsigned char *_blkid_probe_get_sb(blkid_probe pr, const struct blkid_idmag *mag, size_t size)
{
uint64_t hint_offset;
if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
hint_offset = 0;
return blkid_probe_get_buffer(pr, hint_offset + (mag->kboff << 10), size);
}
/*
* Check for matching magic value.
* Returns BLKID_PROBE_OK if found, BLKID_PROBE_NONE if not found
@ -1017,8 +1064,12 @@ int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
/* try to detect by magic string */
while(mag && mag->magic) {
unsigned char *buf;
uint64_t hint_offset;
off = (mag->kboff + (mag->sboff >> 10)) << 10;
if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
hint_offset = 0;
off = hint_offset + ((mag->kboff + (mag->sboff >> 10)) << 10);
buf = blkid_probe_get_buffer(pr, off, 1024);
if (!buf && errno)
@ -2031,3 +2082,123 @@ void blkid_probe_use_wiper(blkid_probe pr, uint64_t off, uint64_t size)
blkid_probe_chain_reset_values(pr, chn);
}
}
static struct blkid_hint *get_hint(blkid_probe pr, const char *name)
{
struct list_head *p;
if (list_empty(&pr->hints))
return NULL;
list_for_each(p, &pr->hints) {
struct blkid_hint *h = list_entry(p, struct blkid_hint, hints);
if (h->name && strcmp(name, h->name) == 0)
return h;
}
return NULL;
}
/**
* blkid_probe_set_hint:
* @pr: probe
* @name: hint name or NAME=value
* @value: offset or another number
*
* Sets extra hint for low-level prober. If the hint is set by NAME=value
* notation than @value is ignored. The functions blkid_probe_set_device()
* and blkid_reset_probe() resets all hints.
*
* The hints are optional way how to force libblkid probing functions to check
* for example another location.
*
* Returns: 0 on success, or -1 in case of error.
*/
int blkid_probe_set_hint(blkid_probe pr, const char *name, uint64_t value)
{
struct blkid_hint *hint = NULL;
char *n = NULL, *v = NULL;
if (strchr(name, '=')) {
char *end = NULL;
if (blkid_parse_tag_string(name, &n, &v) != 0)
goto done;
errno = 0;
value = strtoumax(v, &end, 10);
if (errno || v == end || (end && *end))
goto done;
}
hint = get_hint(pr, n ? n : name);
if (hint) {
/* alter old hint */
hint->value = value;
DBG(LOWPROBE,
ul_debug("updated hint '%s' to %"PRIu64"", hint->name, hint->value));
} else {
/* add a new hint */
if (!n) {
n = strdup(name);
if (!n)
goto done;
}
hint = malloc(sizeof(*hint));
if (!hint)
goto done;
hint->name = n;
hint->value = value;
INIT_LIST_HEAD(&hint->hints);
list_add_tail(&hint->hints, &pr->hints);
DBG(LOWPROBE,
ul_debug("new hint '%s' is %"PRIu64"", hint->name, hint->value));
n = NULL;
}
done:
free(n);
free(v);
if (!hint)
return errno ? -errno : -EINVAL;
return 0;
}
int blkid_probe_get_hint(blkid_probe pr, const char *name, uint64_t *value)
{
struct blkid_hint *h = get_hint(pr, name);
if (!h)
return -EINVAL;
if (value)
*value = h->value;
return 0;
}
/**
* blkid_probe_reset_hints:
* @pr probe
*
* Removes all previously defined probinig hints. See also blkid_probe_set_hint().
*/
void blkid_probe_reset_hints(blkid_probe pr)
{
if (list_empty(&pr->hints))
return;
DBG(LOWPROBE, ul_debug("resetting hints"));
while (!list_empty(&pr->hints)) {
struct blkid_hint *h = list_entry(pr->hints.next,
struct blkid_hint, hints);
list_del(&h->hints);
free(h->name);
free(h);
}
INIT_LIST_HEAD(&pr->hints);
}

View File

@ -171,7 +171,13 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
struct iso_volume_descriptor *iso;
unsigned char label[32];
int i;
int off;
uint64_t off;
if (blkid_probe_get_hint(pr, mag->hoff, &off) < 0)
off = 0;
if (off % 2048)
return 1;
if (strcmp(mag->magic, "CDROM") == 0)
return probe_iso9660_hsfs(pr, mag);
@ -201,7 +207,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
probe_iso9660_set_uuid(pr, &iso->created);
/* Joliet Extension and Boot Record */
off = ISO_VD_OFFSET;
off += ISO_VD_OFFSET;
for (i = 0; i < ISO_VD_MAX; i++) {
struct boot_record *boot= (struct boot_record *)
blkid_probe_get_buffer(pr,
@ -267,8 +273,8 @@ const struct blkid_idinfo iso9660_idinfo =
.flags = BLKID_IDINFO_TOLERANT,
.magics =
{
{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9 },
{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9, .hoff = "session_offset" },
{ NULL }
}
};

View File

@ -182,6 +182,7 @@ static int probe_udf(blkid_probe pr,
struct logical_vol_integ_descriptor_imp_use *lvidiu;
uint32_t lvid_len = 0;
uint32_t lvid_loc = 0;
uint64_t s_off;
uint32_t bs;
uint32_t b;
uint16_t type;
@ -197,6 +198,10 @@ static int probe_udf(blkid_probe pr,
int have_volid = 0;
int have_volsetid = 0;
/* Session offset */
if (blkid_probe_get_hint(pr, "session_offset", &s_off) < 0)
s_off = 0;
/* The block size of a UDF filesystem is that of the underlying
* storage; we check later on for the special case of image files,
* which may have any block size valid for UDF filesystem */
@ -208,13 +213,18 @@ static int probe_udf(blkid_probe pr,
if (i != 0 && pbs[0] == pbs[i])
continue;
/* Do not try with block size which is not divisor of session offset */
if (s_off % pbs[i])
continue;
/* ECMA-167 2/8.4, 2/9.1: Each VSD is either 2048 bytes long or
* its size is same as blocksize (for blocksize > 2048 bytes)
* plus padded with zeros */
vsd_len = pbs[i] > 2048 ? pbs[i] : 2048;
/* Process 2048 bytes long VSD only once */
if (vsd_len == 2048) {
/* Process 2048 bytes long VSD on first session only once
* as its location is same for any blocksize */
if (s_off == 0 && vsd_len == 2048) {
if (vsd_2048_valid == 0)
continue;
if (vsd_2048_valid == 1)
@ -225,7 +235,7 @@ static int probe_udf(blkid_probe pr,
for (b = 0; b < 64; b++) {
vsd = (struct volume_structure_descriptor *)
blkid_probe_get_buffer(pr,
UDF_VSD_OFFSET + b * vsd_len,
s_off + UDF_VSD_OFFSET + b * vsd_len,
sizeof(*vsd));
if (!vsd)
return errno ? -errno : 1;
@ -249,29 +259,41 @@ static int probe_udf(blkid_probe pr,
break;
}
if (vsd_len == 2048)
if (s_off == 0 && vsd_len == 2048)
vsd_2048_valid = 0;
/* NSR was not found, try with next block size */
continue;
anchor:
if (vsd_len == 2048)
if (s_off == 0 && vsd_len == 2048)
vsd_2048_valid = 1;
/* Read Anchor Volume Descriptor (AVDP), detect block size */
vd = (struct volume_descriptor *)
blkid_probe_get_buffer(pr, 256 * pbs[i], sizeof(*vd));
blkid_probe_get_buffer(pr, s_off + 256 * pbs[i], sizeof(*vd));
if (!vd)
return errno ? -errno : 1;
/* Check that we read correct sector and detected correct block size */
if (le32_to_cpu(vd->tag.location) != 256)
continue;
if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 256) {
type = le16_to_cpu(vd->tag.id);
if (type == TAG_ID_AVDP)
goto real_blksz;
}
type = le16_to_cpu(vd->tag.id);
if (type == TAG_ID_AVDP)
goto real_blksz;
/* UDF-2.60: 2.2.3: Unclosed sequential Write-Once media may
* have a single AVDP present at either sector 256 or 512. */
vd = (struct volume_descriptor *)
blkid_probe_get_buffer(pr, s_off + 512 * pbs[i], sizeof(*vd));
if (!vd)
return errno ? -errno : 1;
if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 512) {
type = le16_to_cpu(vd->tag.id);
if (type == TAG_ID_AVDP)
goto real_blksz;
}
}
return 1;
@ -477,13 +499,13 @@ const struct blkid_idinfo udf_idinfo =
.flags = BLKID_IDINFO_TOLERANT,
.magics =
{
{ .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1 },
{ .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
{ NULL }
}
};

View File

@ -659,7 +659,7 @@ int main(int argc, char **argv)
blkid_cache cache = NULL;
char **devices = NULL;
char *search_type = NULL, *search_value = NULL;
char *read = NULL;
char *read = NULL, *hint = NULL;
int fltr_usage = 0;
char **fltr_type = NULL;
int fltr_flag = BLKID_FLTR_ONLYIN;
@ -681,6 +681,7 @@ int main(int argc, char **argv)
{ "label", required_argument, NULL, 'L' },
{ "uuid", required_argument, NULL, 'U' },
{ "probe", no_argument, NULL, 'p' },
{ "hint", required_argument, NULL, 'H' },
{ "info", no_argument, NULL, 'i' },
{ "size", required_argument, NULL, 'S' },
{ "offset", required_argument, NULL, 'O' },
@ -705,7 +706,7 @@ int main(int argc, char **argv)
strutils_set_exitcode(BLKID_EXIT_OTHER);
while ((c = getopt_long (argc, argv,
"c:DdghilL:n:ko:O:ps:S:t:u:U:w:Vv", longopts, NULL)) != -1) {
"c:DdgH:hilL:n:ko:O:ps:S:t:u:U:w:Vv", longopts, NULL)) != -1) {
err_exclusive_options(c, NULL, excl, excl_st);
@ -719,6 +720,9 @@ int main(int argc, char **argv)
case 'D':
ctl.no_part_details = 1;
break;
case 'H':
hint = optarg;
break;
case 'L':
ctl.eval = 1;
search_value = xstrdup(optarg);
@ -869,6 +873,10 @@ int main(int argc, char **argv)
pr = blkid_new_probe();
if (!pr)
goto exit;
if (hint && blkid_probe_set_hint(pr, hint, 0) != 0) {
warn(_("Failed to use probing hint: %s"), hint);
goto exit;
}
if (ctl.lowprobe_superblocks) {
blkid_probe_set_superblocks_flags(pr,