Monday, 18 August 2008

C/C++: Get the CD/DVD volume name

ISO 9660 is a international standard that defines a filesystem for CD/DVD media. To get the volume name from such system in Linux we'll be dealing with the its device path once again. On my system the device path of the CD/DVD device is /dev/scd0.
Also, it is important to know where is this information stored on the CD. Surfing and google-ing around I found the site that explains ISO 9660 in detail: types, modes, sectors etc.
Field of interest (called Volume ID) is located in the sector 16 called Primary Volume Descriptor on the 32808. byte of the CD, and its length is 32 bytes.

Those include directives are important for the compilation:

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #include <fcntl.h>

  4. #include <string.h>

  5. #include <unistd.h>

Next, function that can read chunk of data from the filesystem at specific byte is needed:

  1. int fs_read_data ( char * fs, int seek, int len, char * data)

  2. {

  3. unsigned int fd;

  4. int result = -1;

  5. if ( fs != NULL)

  6. {

  7. if ( ( fd = open (fs, O_RDONLY)) != -1)

  8. {

  9. if ( lseek (fd, seek, SEEK_SET) != -1)

  10. {

  11. if ( read ( fd, data, len) != -1)

  12. {

  13. data[len] = '\0';

  14. result = 0;

  15. }

  16. }

  17. }

  18. close ( fd);

  19. }

  20. return result;

  21. }

Function fs_read_data takes four arguments. String fs is a device path of a iso9660 filesystem, variable seek is a byte number at which we start reading data, len is number of bytes to read and data is a character buffer that stores the read data.
If the fs string is NULL, function returns -1 as a result, otherwise the device file is opened and data is being read from it.

Function open (read more) tries to open the device file representing our filesystem (/dev/scd0). Upon successful completion, the function shall open the file and return a non-negative integer representing the lowest numbered unused file descriptor; otherwise, -1 shall be returned. Second argument of the open function says to open the file for rading only.

Function lseek (read more) at line 9 sets the number of bytes to skip for the current file descriptor. In this case the number 32808 will be passed.

Finally, data is being read with the function read (read more) at line 11. It reads the len number of bytes from the file associated with the fd file descriptor and puts that chunk of bytes into data buffer. In our case the len will be 32.

If all the conditions of the if statements are true the function fs_read_data will return 0 and data will be filled. Character '\0' marks the end of the string.

The main function is now constructed like this:

  1. int main() {

  2. char * fs = "/dev/scd0";

  3. int seek =32808;

  4. int len = 32;

  5. char * buff = NULL;

  6. char * volume_name = NULL;

  7. buff = malloc( (sizeof (buff)) * (len+1));

  8. if ( fs_read_data ( fs, seek, len, buff) != -1)

  9. {

  10. if ( (strncmp ( buff, "NO NAME", 7) == 0) || ( strncmp (buff, " ",1) == 0))

  11. {

  12. free ( buff);

  13. buff=NULL;

  14. volume_name = "None";

  15. }

  16. else

  17. {

  18. volume_name = strdup ( buff);

  19. }

  20. }

  21. printf("%s", volume_name);

  22. free (buff);

  23. buff=NULL;

  24. return 0;

  25. }

All important variables are declared in first 7 lines. Notice that malloc function is used to allocate 32 bytes in the buffer buff.
The if statement at line 9 tries to read wanted Volume ID field from the /dev/scd0 device file representing my CD/DVD device. Next, strcmp function is used to check if the string on that field is empty string ("") or it says "NO NAME". If either of that is true the volume name of the ISO 9660 filesystem isn't set, the buff is freed and "None" is assigned to the volume_name char array. Otherwise, the content of the buff is duplicated into volume_name. At the end volume name is printed on the screen.


iborco said...

Nice code.

Have you find any other way to read the labels?

I have looked all morning for some *nix call or library that can give me the CD/DVD label and your code is the best I've found so far.


Tvrtko said...

@Ionutz Borcoman:
Sorry for late response. Thanks for checking out the code.

Unfortunately, I didn't find any other way except this. Cheers