(root)/
util-linux-2.39/
libblkid/
src/
devno.c
       1  /*
       2   * devno.c - find a particular device by its device number (major/minor)
       3   *
       4   * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
       5   * Copyright (C) 2001 Andreas Dilger
       6   *
       7   * %Begin-Header%
       8   * This file may be redistributed under the terms of the
       9   * GNU Lesser General Public License.
      10   * %End-Header%
      11   */
      12  
      13  #include <stdio.h>
      14  #include <string.h>
      15  #ifdef HAVE_UNISTD_H
      16  #include <unistd.h>
      17  #endif
      18  #include <stdlib.h>
      19  #ifdef HAVE_SYS_TYPES_H
      20  #include <sys/types.h>
      21  #endif
      22  #ifdef HAVE_SYS_STAT_H
      23  #include <sys/stat.h>
      24  #endif
      25  #include <dirent.h>
      26  #ifdef HAVE_ERRNO_H
      27  #include <errno.h>
      28  #endif
      29  #ifdef HAVE_SYS_MKDEV_H
      30  #include <sys/mkdev.h>
      31  #endif
      32  #include <fcntl.h>
      33  #include <inttypes.h>
      34  
      35  #include "blkidP.h"
      36  #include "pathnames.h"
      37  #include "sysfs.h"
      38  #include "strutils.h"
      39  
      40  static char *blkid_strconcat(const char *a, const char *b, const char *c)
      41  {
      42  	char *res, *p;
      43  	size_t len, al, bl, cl;
      44  
      45  	al = a ? strlen(a) : 0;
      46  	bl = b ? strlen(b) : 0;
      47  	cl = c ? strlen(c) : 0;
      48  
      49  	len = al + bl + cl;
      50  	if (!len)
      51  		return NULL;
      52  	p = res = malloc(len + 1);
      53  	if (!res)
      54  		return NULL;
      55  	if (al)
      56  		p = mempcpy(p, a, al);
      57  	if (bl)
      58  		p = mempcpy(p, b, bl);
      59  	if (cl)
      60  		p = mempcpy(p, c, cl);
      61  	*p = '\0';
      62  	return res;
      63  }
      64  
      65  /*
      66   * This function adds an entry to the directory list
      67   */
      68  static void add_to_dirlist(const char *dir, const char *subdir,
      69  				struct dir_list **list)
      70  {
      71  	struct dir_list *dp;
      72  
      73  	dp = malloc(sizeof(struct dir_list));
      74  	if (!dp)
      75  		return;
      76  	dp->name = subdir ? blkid_strconcat(dir, "/", subdir) :
      77  		   dir ? strdup(dir) : NULL;
      78  
      79  	if (!dp->name) {
      80  		free(dp);
      81  		return;
      82  	}
      83  	dp->next = *list;
      84  	*list = dp;
      85  }
      86  
      87  /*
      88   * This function frees a directory list
      89   */
      90  static void free_dirlist(struct dir_list **list)
      91  {
      92  	struct dir_list *dp, *next;
      93  
      94  	for (dp = *list; dp; dp = next) {
      95  		next = dp->next;
      96  		free(dp->name);
      97  		free(dp);
      98  	}
      99  	*list = NULL;
     100  }
     101  
     102  void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list,
     103  		     char **devname)
     104  {
     105  	DIR	*dir;
     106  	struct dirent *dp;
     107  	struct stat st;
     108  
     109  	if ((dir = opendir(dirname)) == NULL)
     110  		return;
     111  
     112  	while ((dp = readdir(dir)) != NULL) {
     113  #ifdef _DIRENT_HAVE_D_TYPE
     114  		if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_BLK &&
     115  		    dp->d_type != DT_LNK && dp->d_type != DT_DIR)
     116  			continue;
     117  #endif
     118  		if (dp->d_name[0] == '.' &&
     119  		    ((dp->d_name[1] == 0) ||
     120  		     ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
     121  			continue;
     122  
     123  		if (fstatat(dirfd(dir), dp->d_name, &st, 0))
     124  			continue;
     125  
     126  		if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
     127  			*devname = blkid_strconcat(dirname, "/", dp->d_name);
     128  			DBG(DEVNO, ul_debug("found 0x%llx at %s", (long long)devno,
     129  				   *devname));
     130  			break;
     131  		}
     132  
     133  		if (!list || !S_ISDIR(st.st_mode))
     134  			continue;
     135  
     136  		/* add subdirectory (but not symlink) to the list */
     137  #ifdef _DIRENT_HAVE_D_TYPE
     138  		if (dp->d_type == DT_LNK)
     139  			continue;
     140  		if (dp->d_type == DT_UNKNOWN)
     141  #endif
     142  		{
     143  			if (fstatat(dirfd(dir), dp->d_name, &st, AT_SYMLINK_NOFOLLOW) ||
     144  			    !S_ISDIR(st.st_mode))
     145  				continue;	/* symlink or fstatat() failed */
     146  		}
     147  
     148  		if (*dp->d_name == '.' || (
     149  #ifdef _DIRENT_HAVE_D_TYPE
     150  		    dp->d_type == DT_DIR &&
     151  #endif
     152  		    strcmp(dp->d_name, "shm") == 0))
     153  			/* ignore /dev/.{udev,mount,mdadm} and /dev/shm */
     154  			continue;
     155  
     156  		add_to_dirlist(dirname, dp->d_name, list);
     157  	}
     158  	closedir(dir);
     159  }
     160  
     161  /* Directories where we will try to search for device numbers */
     162  static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL };
     163  
     164  /**
     165   * SECTION: misc
     166   * @title: Miscellaneous utils
     167   * @short_description: mix of various utils for low-level and high-level API
     168   */
     169  
     170  
     171  
     172  static char *scandev_devno_to_devpath(dev_t devno)
     173  {
     174  	struct dir_list *list = NULL, *new_list = NULL;
     175  	char *devname = NULL;
     176  	const char **dir;
     177  
     178  	/*
     179  	 * Add the starting directories to search in reverse order of
     180  	 * importance, since we are using a stack...
     181  	 */
     182  	for (dir = devdirs; *dir; dir++)
     183  		add_to_dirlist(*dir, NULL, &list);
     184  
     185  	while (list) {
     186  		struct dir_list *current = list;
     187  
     188  		list = list->next;
     189  		DBG(DEVNO, ul_debug("directory %s", current->name));
     190  		blkid__scan_dir(current->name, devno, &new_list, &devname);
     191  		free(current->name);
     192  		free(current);
     193  		if (devname)
     194  			break;
     195  		/*
     196  		 * If we're done checking at this level, descend to
     197  		 * the next level of subdirectories. (breadth-first)
     198  		 */
     199  		if (list == NULL) {
     200  			list = new_list;
     201  			new_list = NULL;
     202  		}
     203  	}
     204  	free_dirlist(&list);
     205  	free_dirlist(&new_list);
     206  
     207  	return devname;
     208  }
     209  
     210  /**
     211   * blkid_devno_to_devname:
     212   * @devno: device number
     213   *
     214   * This function finds the pathname to a block device with a given
     215   * device number.
     216   *
     217   * Returns: a pointer to allocated memory to the pathname on success,
     218   * and NULL on failure.
     219   */
     220  char *blkid_devno_to_devname(dev_t devno)
     221  {
     222  	char *path;
     223  	char buf[PATH_MAX];
     224  
     225  	path = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
     226  	if (path)
     227  		path = strdup(path);
     228  	if (!path)
     229  		path = scandev_devno_to_devpath(devno);
     230  
     231  	if (!path) {
     232  		DBG(DEVNO, ul_debug("blkid: couldn't find devno 0x%04lx",
     233  			   (unsigned long) devno));
     234  	} else {
     235  		DBG(DEVNO, ul_debug("found devno 0x%04llx as %s", (long long)devno, path));
     236  	}
     237  
     238  	return path;
     239  }
     240  
     241  
     242  /**
     243   * blkid_devno_to_wholedisk:
     244   * @dev: device number
     245   * @diskname: buffer to return diskname (or NULL)
     246   * @len: diskname buffer size (or 0)
     247   * @diskdevno: pointer to returns devno of entire disk (or NULL)
     248   *
     249   * This function uses sysfs to convert the @devno device number to the *name*
     250   * of the whole disk. The function DOES NOT return full device name. The @dev
     251   * argument could be partition or whole disk -- both is converted.
     252   *
     253   * For example: sda1, 0x0801 --> sda, 0x0800
     254   *
     255   * For conversion to the full disk *path* use blkid_devno_to_devname(), for
     256   * example:
     257   *
     258   * <informalexample>
     259   *  <programlisting>
     260   *
     261   *	dev_t dev = 0x0801, disk;		// sda1 = 8:1
     262   *	char *diskpath, diskname[32];
     263   *
     264   *	blkid_devno_to_wholedisk(dev, diskname, sizeof(diskname), &disk);
     265   *	diskpath = blkid_devno_to_devname(disk);
     266   *
     267   *	// print "0x0801: sda, /dev/sda, 8:0
     268   *	printf("0x%x: %s, %s, %d:%d\n",
     269   *		dev, diskname, diskpath, major(disk), minor(disk));
     270   *
     271   *	free(diskpath);
     272   *
     273   *  </programlisting>
     274   * </informalexample>
     275   *
     276   * Returns: 0 on success or -1 in case of error.
     277   */
     278  int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
     279  			size_t len, dev_t *diskdevno)
     280  {
     281  	return sysfs_devno_to_wholedisk( dev, diskname, len, diskdevno);
     282  }
     283  
     284  /*
     285   * Returns 1 if the @major number is associated with @drvname.
     286   */
     287  int blkid_driver_has_major(const char *drvname, int drvmaj)
     288  {
     289  	FILE *f;
     290  	char buf[128];
     291  	int match = 0;
     292  
     293  	f = fopen(_PATH_PROC_DEVICES, "r" UL_CLOEXECSTR);
     294  	if (!f)
     295  		return 0;
     296  
     297  	while (fgets(buf, sizeof(buf), f)) {	/* skip to block dev section */
     298  		if (strncmp("Block devices:\n", buf, sizeof(buf)) == 0)
     299  			break;
     300  	}
     301  
     302  	while (fgets(buf, sizeof(buf), f)) {
     303  		int maj;
     304  		char name[64 + 1];
     305  
     306  		if (sscanf(buf, "%d %64[^\n ]", &maj, name) != 2)
     307  			continue;
     308  
     309  		if (maj == drvmaj && strcmp(name, drvname) == 0) {
     310  			match = 1;
     311  			break;
     312  		}
     313  	}
     314  
     315  	fclose(f);
     316  
     317  	DBG(DEVNO, ul_debug("major %d %s associated with '%s' driver",
     318  			drvmaj, match ? "is" : "is NOT", drvname));
     319  	return match;
     320  }
     321  
     322  #ifdef TEST_PROGRAM
     323  int main(int argc, char** argv)
     324  {
     325  	char	*devname, *tmp;
     326  	char	diskname[PATH_MAX];
     327  	int	devmaj, devmin;
     328  	dev_t	devno, disk_devno;
     329  	const char *errmsg = "Couldn't parse %s: %s\n";
     330  
     331  	blkid_init_debug(BLKID_DEBUG_ALL);
     332  	if ((argc != 2) && (argc != 3)) {
     333  		fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
     334  			"Resolve a device number to a device name\n",
     335  			argv[0], argv[0]);
     336  		exit(1);
     337  	}
     338  	if (argc == 2) {
     339  		devno = strtoul(argv[1], &tmp, 0);
     340  		if (*tmp) {
     341  			fprintf(stderr, errmsg, "device number", argv[1]);
     342  			exit(1);
     343  		}
     344  	} else {
     345  		devmaj = strtoul(argv[1], &tmp, 0);
     346  		if (*tmp) {
     347  			fprintf(stderr, errmsg, "major number", argv[1]);
     348  			exit(1);
     349  		}
     350  		devmin = strtoul(argv[2], &tmp, 0);
     351  		if (*tmp) {
     352  			fprintf(stderr, errmsg, "minor number", argv[2]);
     353  			exit(1);
     354  		}
     355  		devno = makedev(devmaj, devmin);
     356  	}
     357  	printf("Looking for device 0x%04llx\n", (long long)devno);
     358  	devname = blkid_devno_to_devname(devno);
     359  	free(devname);
     360  
     361  	printf("Looking for whole-device for 0x%04llx\n", (long long)devno);
     362  	if (blkid_devno_to_wholedisk(devno, diskname,
     363  				sizeof(diskname), &disk_devno) == 0)
     364  		printf("found devno 0x%04llx as /dev/%s\n", (long long) disk_devno, diskname);
     365  
     366  	return 0;
     367  }
     368  #endif