[ Table Of Contents ][ Answer Guy Current Index ] greetings   Meet the Gang   1   2   3   4   5   6   7   8   9 [ Index of Past Answers ]

(?) The Answer Gang (!)

By Jim Dennis, Ben Okopnik, Dan Wilder, Breen, Chris, and the Gang, the Editors of Linux Gazette... and You!
Send questions (or interesting answers) to tag@lists.linuxgazette.net

There is no guarantee that your questions here will ever be answered. Readers at confidential sites must provide permission to publish. However, you can be published anonymously - just let us know!

TAG Member bios | FAQ | Knowledge base

(!) Getting volume label for CD

Useful scripts and tidbits from Ernesto Hernandez-Novich, Michael Blum, Richard A. Bray

Answered By Ben Okopnik, Mike Ellis

It can be argued that there are some dangers in posting code blocks which are not actually correct. However, I think the thought processes revealed in deciding which tricks to use or not while reading data "closer to the metal" than shells normally go is valuable in and of itself. -- Heather

Greetings from Venezuela.

Someone asked that on a mailing-list I suscribe to; I gave the short-short answer that happens to be in the CD-ROM HOWTO at www.linuxdoc.org. Later I answered with code that gives you the label and some more... <g>

Check out and feel free to reproduce the code sample at


(!) [Ben] Good stuff. Thank you! I've modified it a tiny bit by adding
die "Usage: ", $0 =~ m{([^/]*)$}, " <iso_file|cd_device>\n"
        unless @ARGV && -e $ARGV[0];
at the beginning - just in case I forget how to use it - and modified the "open" to check the return value in case of problems:
open CD, $ARGV[0] or die "Can't open $ARGV[0]: $!\n";
It's great otherwise - I've already got it stowed away as "iso9660info" in my "/usr/local/bin". :)

(?) [Ernesto] If your spanish is rusty, the paragraph above the Perl code reads more or less like:

'Nevertheless, before someone asks "How can I find out who prepared the CD? When? For what company? Does it belong to a multiple-CD set? Which one on the set is it?", and since I know that isn't in the HOWTO, allow me to present a small fragment of (hopefully useful) code. BTW, the comments along de Volume Descriptor are nothing but the appropiate mkisofs options needed to fill the values while creating the ISO image.'

If that sounds harsh is because someone suggested that I didn't know jack about the ISO-9660 filesystem and was quoting HOWTO's to get credit <g> (go figure). And so I made a pun at the end of the message, but only works in spanish.

(!) [Ben] Didn't sound harsh to me - I certainly give (and get!) credit for quoting HOWTOs. The trick is knowing which ones to quote, and which part. Besides, why does it matter where you got the answer as long as it's right?

(?) [Ernesto] BTW, feel free to announce the Venezuelan Linux User's Group mailing list in future installments of LinuxGazette. It's specially well suited for spanish-speaking Linux users, who can suscribe to l-linux emailing majordomo@linux.org.ve; we have our archives available for browsing in http://www.linux.org.ve/archivo complete with a searching form working over three years worth of messages.

Keep up the good work!

(!) [Heather] Thanks. We are definitely seeing an increase in spanish requests and I'm sure our readers will find your list handy.
---- He certainly wasn't the only reader helping out...

(?) [Michael Blum] I just came across in your November issue a question on reading the volume label from a CD. If it's in ISO9660 format, which includes the Joliet type CD your reader was burning, it's actually pretty easy to write a command line tool to read the label.

Here's a bash shell script:

See attached blum-rd_label.bash.txt

Note that the parameter is the device file for the CD, e.g. /dev/hdc, and that the CD does not have to be mounted. You need to be 'root' to run the script.

Here's a C program to do the same thing. I've used this program under both Linux & IBM's AIX.

See attached blum-rd_label.c.txt

The only real advantage of the C program is that when compiled the executable can be made suid to root, allowing you to run the program as a non-root user. Just as with the shell script the parameter is the device file for the CD, and the CD does not have to be mounted.

Hope you find this useful! Thanks for your publication - I've learned a lot from it over the years.

(?) [Richard A. Bray] I finally broke down and read the iso9660 format instead of sleeping the other night.

Here are the basic commands to get the data. It will clean it up later to make sure there is a disk in the drive first, and that no errors have occurred. It should run dd only once to load the CD header into a file. Then report the results out of that.

I don't know what formats will be compatible with this, but it seemed to work fine on all of my Windoze CDs and even my Red Hat install CD. I guess I will have to check and make sure that it will work with UDF format someday.

[root@winserver bin]# cat cdinfo

See attached bray-cdinfo.sh.txt

(!) [Ben] <wince> This is not a good idea. You're hitting the hardware device over and over when you could do it all in one read:
# Make sure that a block device was specified
[ -b "$1" ] && { printf "Usage: ${0##*/} <cd_device>\n"; }

# Read the entire header
data=`dd if=$1 ibs=863 skip=32769 count=1 2>/dev/null`
Now you can let your CD go back to sleep, and extract whatever pieces you wanted from the variable:
echo "FSTYPE: ${data:0:5}"
echo "OSTYPE: ${data:6:32}"
This also lets you cut out the temporary variables.
(!) [Mike] Ben's suggestions got me wondering - did all those clever tricks really work? Unfortunately not, because the CD header format includes a lot of NUL characters (ASCII 0) which bash treats as "end of variable".
(!) [Ben]

ben@Baldur:~$ a="`dd if=/dev/hdc ibs=1 skip=32808 count=863 2>/dev/null`"
ben@Baldur:~$ expr length "$a"
Works for me, Mike. The problem may be that you're not quoting the string - or, quoting the individual chunks (not quoting them is what I use to get rid of the extra whitespace.) I didn't experiment with this all that much, but I tested the solution that I suggested, at least for the first few variables:

data="`dd if=/dev/hdc ibs=1024 skip=32 count=1 2>/dev/null`"

echo "FSTYPE    :" ${data:1:5}
echo "OSTYPE    :" ${data:8:32}
echo "CDNAME    :" ${data:40:32}

provides the output:
ben@Baldur:~$ ./cdinf
FSTYPE    : CD00
(!) [Mike] Here's my version of the CD volume label extractor... the handling of non-UTC timezones is wrong, but otherwise it seems to work OK...

See attached ellis-cdlabel_extractor.bash.txt

(!) [Ben] <gazes admiringly at the data = dd stuff piped through tr line> That is a cute trick, though. <stuffing it away in my own toolbox> Thanks!
An even cheaper way to fold that whitespace: don't quote the variable. "bash" will swallow anything that is defined as the first two characters of $IFS - and that happens to be spaces and tabs.
(!) [Mike] One problem with eating spaces. I need those for the offsets to work. :)
(!) [Ben] That's why you only do that when printing out the individual variables, not for the entire string. The program flow is "get string -> grab chunks via offsets -> print w/o spaces."

(?) Now all I am missing is the cd serial number that Windoze generates. I can't seem to find how to compute that. I may just checksum the first 32K of the drive and use that.

(!) [Ben] I seem to vaguely remember Windows showing some weird number. Are you sure it's not stored in the CD header itself? Note that I'm not saying that it is; I'm just wondering.

(?) OK. Here is my current version of the script. I added error checking to properly return errors if no media or of wrong type.

Thanks to Ben and Mike.

See attached bray-smart_cd_labelreader.sh.txt

(!) [Ben]
dd if=$1 bs 1 skip=32768 count 2048 >/tmp/cdinfo$$ 2>/dev/null
[ ... ]
data=`cat /tmp/cdinfo$$ |tr '[\\000-\\037]' '.*'`
(!) [] Why'd you go and do that? :) The file creation is completely unnecessary, and will leave junk in "/tmp" if your script crashes for any reason. If you want to use that mechanism, simply do it on the fly, like Mike did:

data=`dd if=$1 bs=1024 skip=32 count=1 2>/dev/null|tr '[\000-\037]' '.'`

(?) Well, that is because the pipe to tr will always set $? to 0. Then I wouldn't be able to test for failure of dd. Sorry, but that's the rub.

(!) [Ben] [ -z "$data" ] && { printf "Oops, read failed.\n"; exit; }
:) I think this would be even better. What we really care about is that we have data in $data, right? Best to test the end result - although intermediate tests, in addition to the final one, certainly don't hurt.

(?) If I want to use tr to trap for weird characters, then I will have to store the data somewhere. I suppose it is possible for it to crash before reaching the rm -f /tmp/cdinfo$$ line but, if that does happen I probably have something seriously wrong with tr.

I suppose I could stuff the data in a variable from dd and then echo it to tr, that would work wouldn't it?

(!) [Ben] Well, Mike's contention was that you would lose anything past a null when just assigning it that way. I didn't do any rigorous testing, but I'm willing to believe - "\0"s being the way strings are normally terminated. The one header that I tested didn't chop off short, but it may not have contained any nulls.
BTW, Mike - that "tr" function could stand a bit of twiddling. :) The extra '\'s in your "first list" convert backslashes to '.'s; the '*' in your "second list", as the second character, has the "truncated second list" effect - i.e., all matches other than backslashes will be converted to asterisks. That's probably not what you wanted.
(!) [Mike] Well spotted! Gets that's what you get for lazy quoting (well, it doesn't usually cause any nasty problems!)
(!) [Ben] Thanks! Just a matter of clean code. Although printing out an unquoted "$data" has a very interesting result: it shows the header with all the control chars converted to stars... and immediately followed by a listing of the current dir. Why is only the last asterisk interpolated? <shrug> These are the questions that try men's souls.
I usually try to make sure that my code doesn't do anything that I didn't tell it to do, like hanging out in seedy bars with suspicious characters and drinking till all hours. Gotta watch that stuff, or - bam! - it'll grab your credit card and be buying drinks all around.
As well, since all of the data is in the first K, it's not necessary to grab a 2K block; and since the numbers divide neatly by 1024, it's more effective to have "dd" reading it a K - rather than a byte - at a time.
(!) [Mike] Also a good point, although I'd go one stage further since the CD block size is standardised as 2K, it's probably most clear (and quickest...?) to use

data=`dd if=$1 bs=2048 skip=16 count=1 2>/dev/null|tr '[\000-\037]' '.'`
although I concede that it does read a lot more than is strictly necessary.

(?) Yes... The drivers "probably" optimize the command, but it would be better to use the correct size blocks.

Thanks for the tr tips. I've never used tr before. I guess I'll have to actually read the man page.

(!) [Ben] <grin> "When you have learned to snatch the error code from the trap frame, grasshoppa, it will be time for you to leave." Good luck with your coding.

This page edited and maintained by the Editors of Linux Gazette Copyright © 2002
Published in issue 74 of Linux Gazette January 2002
HTML script maintained by Heather Stern of Starshine Technical Services, http://www.starshine.org/

[ Table Of Contents ][ Answer Guy Current Index ] greetings   Meet the Gang   1   2   3   4   5   6   7   8   9 [ Index of Past Answers ]