Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

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.

Wednesday, 13 August 2008

C/C++: Check the filesystem is mounted

I'll write and explain the function that checks the /etc/mtab file for the information if the given device is mounted. For this the default C FILE pointer and the mntent structure will be used. More information about the mntent struct and what elements it holds see this page. Three includes are needed before the coding starts:

  1. #include <stdio.h>

  2. #include <mntent.h>

  3. #include <string.h>


First line doesn't need much explanation; just notice it is needed for the file i/o. Second line includes mntent.h where the mntent structure and functions are declared and the third line includes string.h for string manipulation functions (see the string.h documentation).
Here is the code of the is_mounted function:


  1. int is_mounted (char * dev_path) {

  2. FILE * mtab = NULL;

  3. struct mntent * part = NULL;

  4. int is_mounted = 0;


  5. if ( ( mtab = setmntent ("/etc/mtab", "r") ) != NULL) {

  6. while ( ( part = getmntent ( mtab) ) != NULL) {

  7. if ( ( part->mnt_fsname != NULL )

  8. && ( strcmp ( part->mnt_fsname, dev_path ) ) == 0 ) {

  9. is_mounted = 1;

  10. }

  11. }

  12. endmntent ( mtab);

  13. }


  14. return is_mounted;

  15. }


In first three lines of the is_mounted function file pointer mtab, mntent structure pointer part and integer is_mounted are declared; we assign NULL value to pointers and a zero to the is_mounted integer (assumption is the device isn't mounted). Program would work without assigning NULLs to pointers but it's there for security reasons.

In the fourth line the setmntent function is introduced. This function takes two arguments, first is the path to the mtab file (it's location is /etc/mtab on most Linux systems) and the second is short string determines how the file is opened by the program. In this case file /etc/mtab is trying to be opened for reading only. setmntent function is similar to the fopen function declared in stdio.h.
Next, the getmntent function takes a file pointer as argument, reads a line from the mtab file and fills the mntent structure.
While loop in this code reads the /etc/mtab file line by line and checks if it contains the required information. We are actually comparing given device path char * dev_path with the each of the device paths inside the mtab file (part->mnt_fsname). We are using function strcmp for this. If the mtab contains it that means the filesystem (device) is mounted and we assign number one to our is_mounted variable. Also the mnt_fsname element also must not be NULL.
After the while loop the function endmntent closes the file system description file mtab.
At the end the is_mounted variable is returned.

Now the famous main function can be constructed like this:

  1. int main() {

  2. if (is_mounted("/dev/scd0")) {

  3. printf("CDROM mounted!\n");

  4. }

  5. else {

  6. printf("CDROM not mounted!\n");

  7. }

  8. return 0;

  9. }


The /dev/scd0 is a device path of my cd/dvd device. This should be the same on all Debian based systems. If the device path of the filesystem is unknown this check can still be performed by comparing the mount path with the mnt_dir element of the mntent structure.