Unix readdir (3) and stat (2) giving different inodes - c

I'm the teacher's assistant for my university's system's programming class. Lately the students have been working on an assignment that involves replicating the program pwd.
Some of the students are noticing what appears to be an inconsistency. When they read the ino from a readdir entry, it gives a different inode from when they stat the same directory. Many of them are asking why.
Shouldn't the directory entry's inode point to the inode to the target directory exists at? If so, why does stat give a different inode?
Here is some sample code to demonstrate:
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#define DIR_NAME "~redacted~"
int getReaddirInode(char* entName)
{
DIR* directory;
struct dirent* entry;
ino_t result;
if ((directory = opendir(".")) == NULL)
{
perror("opendir");
exit(1);
}
while ((entry = readdir(directory)) != NULL)
{
if (strcmp(entName, entry->d_name) == 0)
{
result = entry->d_ino;
break;
}
}
if (entry == NULL)
{
fprintf(stderr, "No such directory: %s.\n", entName);
exit(1);
}
if (closedir(directory) == -1)
{
perror("closedir");
exit(1);
}
return result;
}
int getStatInode(char* entName)
{
struct stat buf;
if (stat(entName, &buf) == -1)
{
perror("stat");
exit(1);
}
return buf.st_ino;
}
int main()
{
if (chdir("/home") == -1)
{
perror("chdir");
return 1;
}
printf("readdir (3) gives an inode of:%9d.\n", getReaddirInode(DIR_NAME));
printf("stat (2) gives an inode of: %9d.\n", getStatInode(DIR_NAME));
return 0;
}
Output:
unix3:~ $ ./a.out
readdir (3) gives an inode of: 4053392.
stat (2) gives an inode of: 69205302.
Edit:
I can confirm that DIR_NAME is a mount point:
unix3:~ $ mount | grep ~redacted~
csc-na01.csc.~redacted~.edu:/student01/student01/0_t/~redacted~ on /home/~redacted~ type nfs (rw,relatime,vers=3,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=129.65.158.8,mountvers=3,mountport=635,mountproto=udp,local_lock=none,addr=129.65.158.8)
Edit2:
The inodes only seem to change at the nfs filesystem changeover. My question is why. What inode is readdir pointing to and what inode is stat pointing to?
Both these inodes have changed in the last 4 hours since I posted this.
I do not have permission to unmount.
I checked another directory mounted from the same address and both the inodes were different from the first directory, suggesting that each directory does have two inodes which are unique to that directory, but I don't understand why.

A directory will have an inode:
$ ls -li
total 4
264332 drwx------ 2 attie attie 4096 Nov 3 22:46 mnt
In this case, the inode of the mnt directory is 264322.
But if we now mount a filesystem on that directory, the inode will appear to change:
$ truncate -s $((5 * 1024 * 1024)) myfs.ext2
$ mkfs.ext2 ./myfs.ext2
mke2fs 1.42.13 (17-May-2015)
Discarding device blocks: done
Creating filesystem with 5120 1k blocks and 1280 inodes
Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
$ sudo mount -o loop myfs.ext2 ./mnt
$ ls -li
total 265
2 drwxr-xr-x 3 root root 1024 Nov 3 22:49 mnt
264339 -rw------- 1 attie attie 5242880 Nov 3 22:49 myfs.ext2
Now, it would appear that the inode for mnt is 2...?! (note that the owner/group has changed too).
If we were to run your application on this directory (removing the chdir() and changing DIR_NAME to mnt), then we get an interesting result:
$ ./test
readdir (3) gives an inode of: 264332.
stat (2) gives an inode of: 2.
readdir() is saying 264332
Which is the inode of the mnt directory, on the underlying filesystem.
stat() is saying 2
Which is the inode of the root of the mounted filesystem.
This makes sense, because readdir() is walking through the nodes of the given directory, returning information for each (but it isn't inspecting them in full)... while stat() is returning information for the full path given, including resolving any mountpoints.
In your case, you have an NFS mount, that remote filesystem is mounted on a directory (which has an inode) on this system... but it is also a directory (which has an inode) on the remote system.
We can prove this further, by doing the following:
$ ls -lia /
total 137
2 drwxr-xr-x 25 root root 4096 Oct 11 17:17 .
2 drwxr-xr-x 25 root root 4096 Oct 11 17:17 ..
2097153 drwxr-xr-x 2 root root 12288 Oct 10 13:36 bin
[...]
As you can see, the root of the filesystem mounted at / also has an inode of 2... inodes are not globally unique, but only unique within the filesystem.
If you're involved in the marking of assignments, then I'd say that both answers are acceptable... This is a pretty subtle and complex thing for students who are probably new to this world to fully comprehend.
The stat() answer can easily be checked with ls -i.
I think that checking the readdir() answer is going to be fiddly without writing a little application (which isn't a problem)... You could employ a bind mount if you have permission:
$ mkdir mnt2
$ sudo mount -o bind . ./mnt2
$ ls -li
total 285
2 drwxr-xr-x 3 root root 1024 Nov 3 22:49 mnt
264319 drwx------ 4 attie attie 4096 Nov 3 23:09 mnt2
264339 -rw------- 1 attie attie 5242880 Nov 3 22:49 myfs.ext2
264346 -rwx------ 1 attie attie 9160 Nov 3 23:00 test
264349 -rw------- 1 attie attie 956 Nov 3 23:00 test.c
$ ls -li mnt2
total 288
264332 drwx------ 2 attie attie 4096 Nov 3 22:46 mnt
264347 drwx------ 2 attie attie 4096 Nov 3 23:09 mnt2
264339 -rw------- 1 attie attie 5242880 Nov 3 22:49 myfs.ext2
264346 -rwx------ 1 attie attie 9160 Nov 3 23:00 test
264349 -rw------- 1 attie attie 956 Nov 3 23:00 test.c

Related

trying to run arbitrary commands and parse their output

here is part of code
scanf("%[^\n]%*c",command);
int pid;
pid=fork();
if (pid == 0) {
// Child process
char *argv[]={command ,NULL};
execvp(argv[0], argv);
exit (0);
}
When I give as input ls I want as output
1 copy of mysh1.c mysh1.c mysh3.c mysh.c New Folder
a.out helpmanual.desktop mysh2.c mysh4.c New File
and when i give ls -l /tmp
i'm waiting
total 12
-rw------- 1 antre antre 0 Nov 4 17:31 config-err-KT9sEZ
drwx------ 2 antre antre 4096 Nov 4 19:21 mozilla_antre0
drwx------ 2 antre antre 4096 Jan 1 1970 orbit-antre
drwx------ 2 antre antre 4096 Nov 4 17:31 ssh-HaOFtKdeIQnQ `
but i take:
1 copy of mysh1.c mysh1.c mysh3.c mysh.c New Folder
a.out helpmanual.desktop mysh2.c mysh4.c New File
It seems that you're trying to parse the output of ls -l in a C program for some reason.
That's unlikely to be the “right” thing to do. The usual mechanism is to use opendir and readdir to read the directory file, directly.
If you have some truly strange situation in which you cannot opendir (the only case that comes to mind is if you're running ls on a remote system, eg, over ssh), there is a mode in GNU ls specifically for producing an output record format that can be parsed by another program.
From the GNU coreutils info:
10.1.2 What information is listed
‘-D’
‘--dired’
With the long listing (‘-l’) format, print an additional line after
the main output:
//DIRED// BEG1 END1 BEG2 END2 ...
The BEGN and ENDN are unsigned integers that record the byte
position of the beginning and end of each file name in the output.
This makes it easy for Emacs to find the names, even when they
contain unusual characters such as space or newline, without fancy
searching.
If directories are being listed recursively (‘-R’), output a
similar line with offsets for each subdirectory name:
//SUBDIRED// BEG1 END1 ...
Finally, output a line of the form:
//DIRED-OPTIONS// --quoting-style=WORD
where WORD is the quoting style (*note Formatting the file
names::).
Here is an actual example:
$ mkdir -p a/sub/deeper a/sub2
$ touch a/f1 a/f2
$ touch a/sub/deeper/file
$ ls -gloRF --dired a
a:
total 8
-rw-r--r-- 1 0 Jun 10 12:27 f1
-rw-r--r-- 1 0 Jun 10 12:27 f2
drwxr-xr-x 3 4096 Jun 10 12:27 sub/
drwxr-xr-x 2 4096 Jun 10 12:27 sub2/
a/sub:
total 4
drwxr-xr-x 2 4096 Jun 10 12:27 deeper/
a/sub/deeper:
total 0
-rw-r--r-- 1 0 Jun 10 12:27 file
a/sub2:
total 0
//DIRED// 48 50 84 86 120 123 158 162 217 223 282 286
//SUBDIRED// 2 3 167 172 228 240 290 296
//DIRED-OPTIONS// --quoting-style=literal
Note that the pairs of offsets on the ‘//DIRED//’ line above
delimit these names: ‘f1’, ‘f2’, ‘sub’, ‘sub2’, ‘deeper’, ‘file’.
The offsets on the ‘//SUBDIRED//’ line delimit the following
directory names: ‘a’, ‘a/sub’, ‘a/sub/deeper’, ‘a/sub2’.
Here is an example of how to extract the fifth entry name,
‘deeper’, corresponding to the pair of offsets, 222 and 228:
$ ls -gloRF --dired a > out
$ dd bs=1 skip=222 count=6 < out 2>/dev/null; echo
deeper
Note that although the listing above includes a trailing slash for
the ‘deeper’ entry, the offsets select the name without the
trailing slash. However, if you invoke ‘ls’ with ‘--dired’ along
with an option like ‘--escape’ (aka ‘-b’) and operate on a file
whose name contains special characters, notice that the backslash
is included:
$ touch 'a b'
$ ls -blog --dired 'a b'
-rw-r--r-- 1 0 Jun 10 12:28 a\ b
//DIRED// 30 34
//DIRED-OPTIONS// --quoting-style=escape
If you use a quoting style that adds quote marks (e.g.,
‘--quoting-style=c’), then the offsets include the quote marks. So
beware that the user may select the quoting style via the
environment variable ‘QUOTING_STYLE’. Hence, applications using
‘--dired’ should either specify an explicit
‘--quoting-style=literal’ option (aka ‘-N’ or ‘--literal’) on the
command line, or else be prepared to parse the escaped names.
i just only needed to use strtok

Error compiling kernel module, fatal error: linux/modversions.h: File or directory not found

i'm trying to compile a simple test kernel module but gcc drop me this error:
fatal error: linux/modversions.h: file or directory not found
but in
/usr/src contains the symlink to the linux souce indeed
ls -l /usr/scr/
drwxr-xr-x 2 root root 4096 apr 26 13:31 fglrx-14.9+ga14.201
lrwxrwxrwx 1 root root 39 lug 11 13:36 linux -> /home/nhoya/Scaricati/linux-source-3.16
drwxr-xr-x 2 root root 4096 lug 10 17:49 linux-config-3.16
drwxr-xr-x 4 root root 4096 lug 10 15:57 linux-headers-3.2.0-4-amd64
drwxr-xr-x 4 root root 4096 apr 26 22:24 linux-headers-3.2.0-4-common
lrwxrwxrwx 1 root root 23 giu 24 2012 linux-kbuild-3.2 -> ../lib/linux-kbuild-3.2
-rw-r--r-- 1 root root 81869308 mag 24 20:24 linux-source-3.16.tar.xz
drwxr-xr-x 12 root root 4096 giu 7 23:31 virtualbox-4.3.18
and
locate modversions.h
/home/nhoya/Scaricati/linux-source-3.16/include/config/modversions.h
/usr/src/linux-headers-3.2.0-4-amd64/include/config/modversions.h
So, i really don't understand the problem... this is the module source
#define __KERNEL__
#define MODULE
#include <linux/modversions.h>
#include <linux/module.h>
#include <linux/tty.h>
int init_mod()
{
console_print("test\n");
return 0;
}
void cleanup_mod()
{
console_print("fine test\n");
}

The influence of file mode when file is read and written by a same user in different processes

This is my code
fd=open("a",O_RDWR | O_CREAT);
printf("%d\n", fd);
if(fd < 0)
{
perror("error");
exit(1);
}
lseek(fd, 0, SEEK_SET);
read(fd, buf, 10);
write(STDOUT_FILENO, buf, 10);
getchar();//1
lseek(fd, 0, SEEK_SET);
write(fd, "xxxxxxxxxx", 10);
getchar();//2
lseek(fd, 0, SEEK_SET);
read(fd, buf, 10);
write(STDOUT_FILENO, buf, 10);
getchar();//3
next is something about file a
//file a, mode 600
//aaaaaaaaaaa
when at step 2, the text of file a will be changed into "xxxxx...".
then I use vim to change the text into "bbbbbbb..." in another terminal.
the output at step 3 is "xxxxx..."
however, when file a is
//file a, mode 606 or 660
//aaaaaaaaaaaa
do same thing as above
the output is "bbbbbbb...."
my system is os x 10.9
I can reproduce the problem, to my considerable surprise (Mac OS X 10.9.4).
However, as I hinted might be a possibility in my comment, the problem seems to be that vim is changing the inode number of the file when the file has 600 permission:
$ for mode in 600 606 660 666
> do
> echo "Mode: $mode"
> echo "abcdefghijklmnopqrst" > a
> chmod $mode a
> ls -li a
> vim a
> cat a
> ls -li a
> done
Mode: 600
25542402 -rw------- 1 jleffler staff 21 Sep 2 07:58 a
xxxxxxxxxxklmnopqrst
25542484 -rw------- 1 jleffler staff 21 Sep 2 07:58 a
Mode: 606
25542484 -rw----rw- 1 jleffler staff 21 Sep 2 07:58 a
xxxxxxxxxxklmnopqrst
25542484 -rw----rw- 1 jleffler staff 21 Sep 2 07:58 a
Mode: 660
25542484 -rw-rw---- 1 jleffler staff 21 Sep 2 07:58 a
xxxxxxxxxxklmnopqrst
25542484 -rw-rw---- 1 jleffler staff 21 Sep 2 07:58 a
Mode: 666
25542484 -rw-rw-rw- 1 jleffler staff 21 Sep 2 07:58 a
xxxxxxxxxxklmnopqrst
25542484 -rw-rw-rw- 1 jleffler staff 21 Sep 2 07:58 a
$
In each case, I ran the command 10rx and :x in vim.
I'm not clear why vim needs to change the inode when the file is 600 permission, but it smacks of a bug from where I'm sitting. It is behaviour I would not have expected at all (except that it explained what you saw).
Because the 'file descriptor' program (the outline code in the question) keeps the same file open, the inode number of the file it is working with does not change, but because vim rewrites the file with a new inode number (meaning: it creates a new file with a new name and inode number containing the modified contents, then removes the old version of a and replaces it with the new file), the edit made by vim (when the file has 600 permission) is not seen in the file that the program has open. At the end of the 'file descriptor' program when the permissions are 600, the file that it had open has no name and its contents are deleted by the system; the file that vim created has taken the place of the original file.

Why do dots ("." and "..") appear when I print files from directory?

I'm printing files from two directories using C language. Here is my code:
char *list1[30], *list2[30];
int i=0, x=0;
struct dirent *ent, *ent1;
/* print all the files and directories within directory */
while ((ent = readdir (dirSource)) != NULL) {
list1[i] = ent->d_name;
i++;
}
i=0;
while((ent1 = readdir (dirDest)) != NULL) {
list2[i] = ent1->d_name;
i++;
}
while(x != i){
printf("Daemon - %s\n", list1[x]);
printf("Daemon1 - %s\n", list2[x]);
x++;
}
I can print all the files, but everytime I print the files in a directory, the end result is this:
Daemon - .
Daemon1 - .
Daemon - ..
Daemon1 - ..
Daemon - fich5
Daemon1 - fich4
Daemon - fich3
Daemon1 - fich3
I don't understand why there are dots in the beginning.
Obs.: I don't if it matters, but I'm using Ubuntu 14.04 on a pen, meaning every time I use Ubuntu, I use the trial instead of using dual boot on my pc.
. and .. are two special files which are in every directory in Linux and other Unix-like systems. . represents the current directory and .. represents the parent directory.
Every directory in Unix has the entry . (meaning current directory) and .. (the parent directory).
Give that they start with "." they are hidden files; ls normally do not show them unless you use "-a" option.
See:
[:~/tmp/lilla/uff] % ls -l
total 0
-rw-rw-r-- 1 romano romano 0 May 17 18:48 a
-rw-rw-r-- 1 romano romano 0 May 17 18:48 b
[:~/tmp/lilla/uff] % ls -la
total 8
drwxrwxr-x 2 romano romano 4096 May 17 18:48 .
drwxrwxr-x 3 romano romano 4096 May 17 18:47 ..
-rw-rw-r-- 1 romano romano 0 May 17 18:48 a
-rw-rw-r-- 1 romano romano 0 May 17 18:48 b

How to map /proc/bus/usb/devices entry to a /dev/sdX device?

I need to know how I can figure out to which entry in /proc/bus/usb/devices a /dev/sdX device maps to. Basically, I need to know the vendor id and product id of a given USB stick (which may not have a serial number).
In my case, I have this entry for my flash drive in /proc/bus/usb/devices:
T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 6 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0781 ProdID=5530 Rev= 2.00
S: Manufacturer=SanDisk
S: Product=Cruzer
S: SerialNumber=0765400A1BD05BEE
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=200mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I happen to know that in my case it is /dev/sda, but I'm not sure how I can figure this out in code. My first approach was to loop through all /dev/sdXX devices and issue a SCSI_IOCTL_GET_BUS_NUMBER and/or SCSI_IOCTL_GET_IDLUN request, but the information returned doesn't help me match it up:
/tmp # ./getscsiinfo /dev/sda
SCSI bus number: 8
ID: 00
LUN: 00
Channel: 00
Host#: 08
four_in_one: 08000000
host_unique_id: 0
I'm not sure how I can use the SCSI bus number or the ID, LUN, Channel, Host to map it to the entry in /proc/bus/usb/devices. Or how I could get the SCSI bus number from the /proc/bus/usb/001/006 device, which is a usbfs device and doesn't appear to like the same ioctl's:
/tmp # ./getscsiinfo /proc/bus/usb/001/006
Could not get bus number: Inappropriate ioctl for device
Here's the test code for my little getscsiinfo test tool:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
struct scsi_idlun
{
int four_in_one;
int host_unique_id;
};
int main(int argc, char** argv) {
if (argc != 2)
return 1;
int fd = open(argv[1], O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
printf("Error opening device: %m\n");
return 1;
}
int busNumber = -1;
if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &busNumber) < 0)
{
printf("Could not get bus number: %m\n");
close(fd);
return 1;
}
printf("SCSI bus number: %d\n", busNumber);
struct scsi_idlun argid;
if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &argid) < 0)
{
printf("Could not get id: %m\n");
close(fd);
return 1;
}
printf("ID: %02x\n", argid.four_in_one & 0xFF);
printf("LUN: %02x\n", (argid.four_in_one >> 8) & 0xFF);
printf("Channel: %02x\n", (argid.four_in_one >> 16) & 0xFF);
printf("Host#: %02x\n", (argid.four_in_one >> 24) & 0xFF);
printf("four_in_one: %08x\n", (unsigned int)argid.four_in_one);
printf("host_unique_id: %d\n", argid.host_unique_id);
close(fd);
return 0;
}
Does anyone have any idea?
udevadm is capable of what your are trying to achieve.
udevadm info -a -p $(udevadm info -q path -n /dev/sda)
udevadm's sources will tell you how it is done.
I believe you can collect such information using libudev library.
Here are some details about it: http://www.signal11.us/oss/udev/
I found something like this on above site:
.. Using libudev, we'll be able to inspect the devices, including their Vendor ID (VID), Product ID (PID), serial number, and device strings, without ever opening the device. Further, libudev will tell us exactly where inside /dev the device's node is located, giving the application a robust and distribution-independent way of accessing the device. ...
This isn't all that easy, nor very well documented (at least from a high-level perspective). The following should work in Kernel's from version 3.1 upward (at least).
I have found the easiest (probably not the only way) is to navigate from the block device entry and test each block device until you find the one that matches your USB entry.
For example, given a block device in /sys/block, such as sdb, you can find the hardware device descriptor entry like this:
# cd /sys/block
# cd $(readlink sdb); cd ../../../../../..
# ls -l
total 0
drwxr-xr-x 6 root root 0 Aug 14 10:47 1-1:1.0
-rw-r--r-- 1 root root 4096 Aug 14 10:52 authorized
-rw-r--r-- 1 root root 4096 Aug 14 10:52 avoid_reset_quirk
-r--r--r-- 1 root root 4096 Aug 14 10:47 bcdDevice
-rw-r--r-- 1 root root 4096 Aug 14 10:49 bConfigurationValue
-r--r--r-- 1 root root 4096 Aug 14 10:47 bDeviceClass
-r--r--r-- 1 root root 4096 Aug 14 10:49 bDeviceProtocol
-r--r--r-- 1 root root 4096 Aug 14 10:49 bDeviceSubClass
-r--r--r-- 1 root root 4096 Aug 14 10:49 bmAttributes
-r--r--r-- 1 root root 4096 Aug 14 10:49 bMaxPacketSize0
-r--r--r-- 1 root root 4096 Aug 14 10:49 bMaxPower
-r--r--r-- 1 root root 4096 Aug 14 10:49 bNumConfigurations
-r--r--r-- 1 root root 4096 Aug 14 10:49 bNumInterfaces
-r--r--r-- 1 root root 4096 Aug 14 10:49 busnum
-r--r--r-- 1 root root 4096 Aug 14 10:52 configuration
-r--r--r-- 1 root root 65553 Aug 14 10:47 descriptors
-r--r--r-- 1 root root 4096 Aug 14 10:52 dev
-r--r--r-- 1 root root 4096 Aug 14 10:49 devnum
-r--r--r-- 1 root root 4096 Aug 14 10:52 devpath
lrwxrwxrwx 1 root root 0 Aug 14 10:47 driver -> ../../../../../../bus/usb/drivers/usb
drwxr-xr-x 3 root root 0 Aug 14 10:52 ep_00
-r--r--r-- 1 root root 4096 Aug 14 10:47 idProduct
-r--r--r-- 1 root root 4096 Aug 14 10:47 idVendor
-r--r--r-- 1 root root 4096 Aug 14 10:52 ltm_capable
-r--r--r-- 1 root root 4096 Aug 14 10:47 manufacturer
-r--r--r-- 1 root root 4096 Aug 14 10:49 maxchild
lrwxrwxrwx 1 root root 0 Aug 14 10:52 port -> ../1-0:1.0/port1
drwxr-xr-x 2 root root 0 Aug 14 10:52 power
-r--r--r-- 1 root root 4096 Aug 14 10:47 product
-r--r--r-- 1 root root 4096 Aug 14 10:52 quirks
-r--r--r-- 1 root root 4096 Aug 14 10:47 removable
--w------- 1 root root 4096 Aug 14 10:52 remove
-r--r--r-- 1 root root 4096 Aug 14 10:47 serial
-r--r--r-- 1 root root 4096 Aug 14 10:49 speed
lrwxrwxrwx 1 root root 0 Aug 14 10:47 subsystem -> ../../../../../../bus/usb
-rw-r--r-- 1 root root 4096 Aug 14 10:47 uevent
-r--r--r-- 1 root root 4096 Aug 14 10:52 urbnum
-r--r--r-- 1 root root 4096 Aug 14 10:49 version
(You can find excellent documentation for the contents of the USB Descriptor here on the BeyondLogic site.)
Given the above, you should be able to map one or more of the USB device fields to the contents of /proc/bus/usb/devices. I find that the serial number is the easiest to match on, so that if you were to cat serial above, you would get the same serial number as listed:
T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=0781 ProdID=5575 Rev=01.26
S: Manufacturer=SanDisk
S: Product=Cruzer Glide
S: SerialNumber=4C530100801115115112
C: #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=200mA
I: If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
If you go to /sys/block, you can list the full path to the host device entry in the storage driver sysfs entry for each device. Typically, I do this using some programmatic means instead of at the shell prompt, but here you can see the links themselves:
# ls -l sd*
lrwxrwxrwx 1 root root 0 Aug 14 10:45 sda -> ../devices/pci0000:00/0000:00:10.0/host32/target32:0:0/32:0:0:0/block/sda
lrwxrwxrwx 1 root root 0 Aug 14 10:47 sdb -> ../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host33/target33:0:0/33:0:0:0/block/sdb
Note that you mustn't make any assumptions about the numbers you see in the links. Depending upon the bus subsystem, the mappings could be quite different. For example, on a Raspberry Pi, it looks like this:
# ls -l sd*
lrwxrwxrwx 1 root root 0 Aug 13 23:54 sda -> ../devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4/1-1.4:1.0/host3/target3:0:0/3:0:0:0/block/sda
lrwxrwxrwx 1 root root 0 Aug 13 23:54 sdb -> ../devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/host4/target4:0:0/4:0:0:0/block/sdb
So, the best approach is to take the approach listed at the top and navigate relative to the storage driver to find the USB device descriptor.
I'd be curious about more authoritative answers to this. The method above was arrived at by trial-and-error but has been working on several different devices and Kernels with no problem.
Instead of using proc/bus/usb which is for usbfs you can use /proc/scsi/scsi. In there you can find the Vendor and Serial number with specific channel ID and LUN number.

Resources