首先看看/sbin/mdev的帮助
/sbin/mdev --help
BusyBox v1.16.1 (2011-08-29 15:29:53 HKT) multi-call binary.
Usage: mdev [-s]
        -s      Scan /sys and populate /dev during system boot
It can be run by kernel as a hotplug helper. To activate it:
 echo /sbin/mdev > /proc/sys/kernel/hotplug
It uses /etc/mdev.conf with lines
[-]DEVNAME UID:GID PERM [>|=PATH] [@|$|*PROG]
 
medv.conf中会定义需要检测的设备文件:
 
# Provide user, group, and mode information for devices.  If a regex matches
# the device name provided by sysfs, use the appropriate user:group and mode
# instead of the default 0:0 660.
#
# Syntax:
# [-]devicename_regex user:group mode [>|=path] [@|$|*cmd args...]
#
# =: move, >: move and create a symlink
# @|$|*: run $cmd on delete, @cmd on create, *cmd on both
null 0:0 0666
zero 0:0 0666
console 0:5 600
ttyS[0-9]* 0:0 0660
event[0-9]+  0:0 0640 =input/
mice         0:0 0640 =input/
mouse[0-9]+  0:0 0640 =input/
sd[a-z][0-9]* 0:0 0660 */etc/automount
[1-9]-[1-9]* 0:0 0660 */etc/automount
 
在这里我们关心U盘和wifi dongle的事件,wifi的dongle和其他usb设备一样都会在/dev下建立[1-9]-[1-9]* 的设备,当相关事件发生时,调用automount
 
在automount中通过getenv()我们去判断$MDEV和$ACTION两个环境变量.,当然也可以用对应设备在sysfs下的event文件中的其他变量去做某些识别比如DEVTYPE:
对于U盘我们可以看到uevent如下:
# cat /sys/block/sda/uevent
MAJOR=8
MINOR=0
DEVNAME=sda
DEVTYPE=disk
 
对于wifi我使用了一个笨办法,如果action是add,那么尝试读以下两个文件
/sys/bus/usb/devices/$MDEV/manufacturer
/sys/bus/usb/devices/$MDEV/product
如果能找到"Manufacturer Realtek","11n Adapter",那么说明确实是wifi dongle
对于拔除消息,由于wifi dongle不像普通优盘那样会有sd*的设备文件去除的消息,只有普通usb诸如[1-9]-[1-9]*的消息,所以我们在检测到插入的时候,把$MDEV变量存成一个文件,在有拔除消息的时候和当前的$MDEV比较,如果一样则说明是wifi dongle拔除。
 
在调试的时候由于automount不在当前shell执行,所以打印看不到,那么一个办法是将调试信息写入文件,然后tail -f filename 去实时打印文件最新内容.另外也可以在脚本中直接将打印输出到控制台,这样更为方便
sd[a-z]  0:0 660 */bin/echo “hello hotplug!” $MDEV $DEVTYPE $ACTION > /dev/console
 
如果automount里面有死机情况,那么可以使用strace来跟踪记录,将hotplug脚本替换如下,这会将mdev执行中所有变量存入/tmp/dbgMdev,同时strace会将运行时所有log存入log.txt
#!/bin/sh
set >/tmp/dbgMdev
echo "set ok==" >> /tmp/dbgMdev
echo >/tmp/dbgMdev">$@>>/tmp/dbgMdev
echo "prepare start mdev" >> /tmp/dbgMdev
#/bin/busybox mdev $*
#echo "end mdev" >> /tmp/dbgMdev
#set>>/tmp/MDEV
#echo >/tmp/MDEV">$@>>/tmp/MDEV
exec /sbin/strace -f -v -s 1024 -o /tmp/log.txt /bin/busybox mdev $*#
 
在2.6.15内核以后,系统也提供netlink接口通过socket消息来捕捉hotplug消息:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>
static int init_hotplug_sock(void)
{
    struct sockaddr_nl snl;
    const int buffersize = 16 * 1024 * 1024;
    int retval;
    memset(&snl, 0x00, sizeof(struct sockaddr_nl));
    snl.nl_family = AF_NETLINK;
    snl.nl_pid = getpid();
    snl.nl_groups = 1;
    int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    if (hotplug_sock == -1)
    {
        printf("error getting socket: %s", strerror(errno));
        return -1;
    }
    /* set receive buffersize */
    setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
    retval = bind(hotplug_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl));
    if (retval < 0) {
        printf("bind failed: %s", strerror(errno));
        close(hotplug_sock);
        hotplug_sock = -1;
        return -1;
    }
    return hotplug_sock;
}
#define UEVENT_BUFFER_SIZE      2048
int main(int argc, char* argv[])
{
         int hotplug_sock       = init_hotplug_sock();
         while(1)
         {
                   char buf[UEVENT_BUFFER_SIZE*2] = {0};
                   recv(hotplug_sock, &buf, sizeof(buf), 0); 
                   printf("%s\n", buf);
         }
         return 0;
}
 
编译运行以后,拔插优盘:
add@/devices/platform/usb-ip9028.1/usb2/2-1
add@/devices/platform/usbscsi6 : usb-storage 2-1:1.0
-ip9028.1/usb2/2-1/2-1:1.0
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/scsi_host/host6
add@/devices/platform/usb-ip9028.1/usb2/2-1/usb_device/usbdev2.9
scsi 6:0:0:0: Direct-Access     SanDisk  U3 Cruzer Micro  3.27 PQ: 0 ANSI: 2
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0
sd 6:0:0:0: Attached scsi generic sg0 type 0
add@/devisd 6:0:0:0: [sda] 4013710 512-byte logical blocks: (2.05 GB/1.91 GiB)
ces/platform/usb-ip9028.1/usb2/2sd 6:0:0:0: [sda] Write Protect is off
-1/2-1:1.0/host6/target6:0:0/6:0sd 6:0:0:0: [sda] Assuming drive cache: write through
:0:0/scsi_disk/6:0:0:0
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_device/6:0:0:0
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:sd 6:0:0:0: [sda] Assuming drive cache: write through
0:0:0/scsi_generic/sg0
change@/ sda:devices/platform/usb-ip9028.1/us sda1
b2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sda
sd 6:0:0:0: [sda] Assuming drive cache: write through
sd 6:0:0:0: [sda] Attached SCSI removable disk
add@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sda/sda1
add@/devices/virtual/bdi/8:0
usb 2-1: USB disconnect, address 9
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_generic/sg0
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_device/6:0:0:0
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/scsi_disk/6:0:0:0
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sda/sda1
remove@/devices/virtual/bdi/8:0
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0/block/sda
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0/6:0:0:0
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/target6:0:0
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6/scsi_host/host6
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0/host6
remove@/devices/platform/usb-ip9028.1/usb2/2-1/2-1:1.0
remove@/devices/platform/usb-ip9028.1/usb2/2-1/usb_device/usbdev2.9
remove@/devices/platform/usb-ip9028.1/usb2/2-1
 
但是对于冷拔插,还没有想到办法如何检测,初步想法是根据/dev下的设备文件([1-9]-[1-9]*)去查看/sys/bus/usb/devices/%s/manufacturer 和 product字段如果和"Manufacturer Realtek"  "11n Adapter"匹配则认为wifi存在
///depth is only used of indent level for print
int chech_wifi_dev(char * dir, int depth)
{
 DIR *dp;
    struct dirent *entry;
    struct stat statbuf;
 int ret = 0;
 
    if((dp = opendir(dir)) == NULL) {
        fprintf(stderr,"cannot open directory: %s\n", dir);
        return ret;
    }
    chdir(dir);
    while((entry = readdir(dp)) != NULL) {
        lstat(entry->d_name,&statbuf);
        if(S_ISDIR(statbuf.st_mode)) {
            /**//* Found a directory, but ignore . and .. */
            if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
                continue;
            //printf("%*s%s/\n",depth,"",entry->d_name);
            /**//* Recurse at a new indent level */
            chech_wifi_dev(entry->d_name,depth+4);
        }
        else
        {
         //printf("%*s%s\n",depth,"",entry->d_name);
         //find [1-9]-[1-9]* 
            if(((entry->d_name[0] <= 0x39)&&(entry->d_name[0] >= 0x31))&&(entry->d_name[1]=='-')&&((entry->d_name[2] <= 0x39)&&(entry->d_name[2] >= 0x31)))
            {
             //printf("find [1-9]-[1-9]*\n");
          
          if(check_wifi(entry->d_name))
          {
           ret = 1;
           break;
          }
         }
     }
    }
    chdir("..");
    closedir(dp);
   
    return ret;
}
 
另外的方法是查看/sys/class/net/wlan0是否存在来判断
if(argc > 1 && strcmp(argv[1], "check-wifi") == 0)
    {
     if(access("/sys/class/net/wlan0/carrier", F_OK)==0)
     {
      iEvent = EVENT_WIRELESS_ADD;
   setWirelessDev("");
         goto send;
     }
    }
 
对于卷标的处理,由于不同文件系统获得卷标的命令不统一,十分麻烦,对于FAT和NTFS通过读FAT表的方式来获得:
static bool GetNTFSName(char *dev_name, char *volumename, int len, int *pActualLen)
{
 int errNo;
// UINT32 fd;
 FILE *fd;
 unsigned short int BytePerSec;
 unsigned char SecPerClus;
 unsigned long long int mft, bak;
 unsigned int record_size;
 const unsigned int bsize = 8192;
 unsigned char buffer[bsize], i;
// MFT_RECORD *mft_record;
 unsigned char *attr_record;
 unsigned int mft_offset;
 unsigned int attr_offset;
 unsigned int record_type, record_data_resident_value_length, record_data_resident_value_offset;
 if (pActualLen == NULL)
  return false;
// fd = open(dev_name, O_RDONLY);
 fd = fopen(dev_name, "ro");
 if (fd <= 0) {
  *pActualLen = 0;
  //strcpy(volumename,  "N\0T\0F\0S\0\0\0");
  //*pActualLen = 4 * 2;  
//  close(fd);
//  fclose(fd);
  DebugPrint((fpDbg, "GetNTFSName fail 1!\n"));
  return false;
 }
// if (read(fd, buffer, bsize) != bsize) {
 if (fread(buffer, 1, bsize, fd) != bsize) {
  *pActualLen = 0;
  //strcpy(volumename,  "N\0T\0F\0S\0\0\0");
  //*pActualLen = 4 * 2;  
//  close(fd);
//  fclose(fd);
  DebugPrint((fpDbg, "GetNTFSName fail 2!\n"));
  return false;
 }
 BytePerSec = (unsigned short int)((unsigned short int)buffer[0xb]&0xff) | ((unsigned short int)buffer[0x0c]<<8);
 SecPerClus = buffer[0xd];
 
 mft = ((unsigned long long int)buffer[0x30]&0xff) | (((unsigned long long int)buffer[0x31]<<8)&0xff00) | (((unsigned long long int)buffer[0x32]<<16)&0xff0000) | (((unsigned long long int)buffer[0x33]<<24)&0xff000000) | (((unsigned long long int)buffer[0x34]<<32)&0xff00000000) | (((unsigned long long int)buffer[0x35]<<40)&0xff0000000000) | (((unsigned long long int)buffer[0x36]<<48)&0xff000000000000) | (((unsigned long long int)buffer[0x37]<<56)&0xff00000000000000);
 bak = (unsigned long long int)((unsigned long long int)buffer[0x38]&0xff) | ((unsigned long long int)buffer[0x39]<<8) | ((unsigned long long int)buffer[0x3A]<<16) | ((unsigned long long int)buffer[0x3B]<<24) | ((unsigned long long int)buffer[0x3C]<<32) | ((unsigned long long int)buffer[0x3D]<<40) | ((unsigned long long int)buffer[0x3E]<<48) | ((unsigned long long int)buffer[0x3F]<<56);
 record_size = ((unsigned int)buffer[0x40]&0xff) | ((unsigned int)buffer[0x41]<<8) | ((unsigned int)buffer[0x42]<<16) | ((unsigned int)buffer[0x43]<<24);
 
 DebugPrint((fpDbg, "GetNTFSName...BytePerSec: %d \n", BytePerSec));
 DebugPrint((fpDbg, "GetNTFSName...SecPerClus: %d \n", SecPerClus));
 DebugPrint((fpDbg, "GetNTFSName...MFT: %lld \n", mft));
 DebugPrint((fpDbg, "GetNTFSName...BAK: %lld \n", bak));
 DebugPrint((fpDbg, "GetNTFSName...record_size: %d \n", record_size));
 
// errNo = lseek(fd, (off_t)mft*BytePerSec*SecPerClus, SEEK_SET);
// errNo = fseeko(fd, (off_t)mft*BytePerSec*SecPerClus, SEEK_SET);
 errNo = fseeko(fd, (long long int)mft*BytePerSec*SecPerClus, SEEK_SET);
 //DebugPrint((fpDbg, "GetNTFSName...(off_t)mft*BytePerSec*SecPerClus: %lld \n", (off_t)mft*BytePerSec*SecPerClus));
 DebugPrint((fpDbg, "GetNTFSName...(long long int)mft*BytePerSec*SecPerClus: %lld \n", (long long int)mft*BytePerSec*SecPerClus));
 if (errNo == -1) {
  printf("Seek Error [%d]\n", errNo);
  *pActualLen = 0;
  //strcpy(volumename,  "N\0T\0F\0S\0\0\0");
  //*pActualLen = 4 * 2;  
//  close(fd);
  fclose(fd);
  DebugPrint((fpDbg, "GetNTFSName fail 3!\n"));
  return false;
 }
// if (read(fd, buffer, bsize) != bsize) {
 if (fread(buffer, 1, bsize, fd) != bsize) {
  *pActualLen = 0;
  //strcpy(volumename,  "N\0T\0F\0S\0\0\0");
  //*pActualLen = 4 * 2;  
//  close(fd);
  fclose(fd);
  DebugPrint((fpDbg, "GetNTFSName fail 4!\n"));
  return false;
 }
 // $MFT
 mft_offset = 0;
// mft_record = (MFT_RECORD *)&buffer[mft_offset];
// attr_offset = mft_offset+mft_record->attrs_offset;
 attr_offset = mft_offset + ((unsigned short int)buffer[mft_offset+20]&0xff) + ((unsigned short int)buffer[mft_offset+21]<<8);
// attr_record = (ATTR_RECORD *)&buffer[attr_offset];
 attr_record = &buffer[attr_offset];
 while (*(unsigned int *)attr_record != 0xffffffff) {
  attr_offset += (attr_record[4]&0xff) + (attr_record[5]<<8) + (attr_record[6]<<16) + (attr_record[7]<<24);
  attr_record = &buffer[attr_offset];
 }
 // $MFTMirr
 mft_offset += (buffer[mft_offset+28]&0xff) + (buffer[mft_offset+29]<<8) + (buffer[mft_offset+30]<<16) + (buffer[mft_offset+31]<<24);
// mft_record = (MFT_RECORD *)&buffer[mft_offset];
// attr_offset = mft_offset+mft_record->attrs_offset;
 attr_offset = mft_offset + ((unsigned short int)buffer[mft_offset+20]&0xff) + ((unsigned short int)buffer[mft_offset+21]<<8);
// attr_record = (ATTR_RECORD *)&buffer[attr_offset];
 attr_record = &buffer[attr_offset];
 while (*(unsigned int *)attr_record != 0xffffffff) {
  attr_offset += (attr_record[4]&0xff) + (attr_record[5]<<8) + (attr_record[6]<<16) + (attr_record[7]<<24);
  attr_record = &buffer[attr_offset];
 }
 // $LogFile
 mft_offset += (buffer[mft_offset+28]&0xff) + (buffer[mft_offset+29]<<8) + (buffer[mft_offset+30]<<16) + (buffer[mft_offset+31]<<24);
// mft_record = (MFT_RECORD *)&buffer[mft_offset];
// attr_offset = mft_offset+mft_record->attrs_offset;
 attr_offset = mft_offset + ((unsigned short int)buffer[mft_offset+20]&0xff) + ((unsigned short int)buffer[mft_offset+21]<<8);
// attr_record = (ATTR_RECORD *)&buffer[attr_offset];
 attr_record = &buffer[attr_offset];
 while (*(unsigned int *)attr_record != 0xffffffff) {
  attr_offset += (attr_record[4]&0xff) + (attr_record[5]<<8) + (attr_record[6]<<16) + (attr_record[7]<<24);
  attr_record = &buffer[attr_offset];
 }
 // $Volume
 mft_offset += (buffer[mft_offset+28]&0xff) + (buffer[mft_offset+29]<<8) + (buffer[mft_offset+30]<<16) + (buffer[mft_offset+31]<<24);
// mft_record = (MFT_RECORD *)&buffer[mft_offset];
// attr_offset = mft_offset+mft_record->attrs_offset;
 attr_offset = mft_offset + ((unsigned short int)buffer[mft_offset+20]&0xff) + ((unsigned short int)buffer[mft_offset+21]<<8);
// attr_record = (ATTR_RECORD *)&buffer[attr_offset];
 attr_record = &buffer[attr_offset];
 while (*(unsigned int *)attr_record != 0xffffffff) {
  record_type = (attr_record[0]&0xff) + (attr_record[1]<<8) + (attr_record[2]<<16) + (attr_record[3]<<24);
  if (record_type == 0x60) {
   record_data_resident_value_length = (attr_record[16]&0xff) + (attr_record[17]<<8) + (attr_record[18]<<16) + (attr_record[19]<<24);
   if (record_data_resident_value_length <= len)
    len = record_data_resident_value_length;
   record_data_resident_value_offset = (attr_record[20]&0xff) + (attr_record[21]<<8);
   ///for (i=0;i<(len>>1);i++)
    ///volumename[i] = buffer[attr_offset+record_data_resident_value_offset + i*2];
   if (len != 0) { 
   memcpy(volumename, &buffer[attr_offset+record_data_resident_value_offset], len);//unicode16
    *pActualLen = len;    
   } else {
    *pActualLen = 0;
    //strcpy(volumename, "N\0T\0F\0S\0\0\0");
    //*pActualLen = 4 * 2;
   }
       
   break;
  }
  attr_offset += (attr_record[4]&0xff) + (attr_record[5]<<8) + (attr_record[6]<<16) + (attr_record[7]<<24);
  attr_record = &buffer[attr_offset];
 }
// close(fd);
 fclose(fd);
 return true;
}
 
fseeko总是出错,应该是因为int64的原因,暂时没解决。
 
static bool GetFATName(char *dev_name, char *volumename, int len)
{
// int   fd;
 FILE   *fd;
 short   f16_size;
 const int  bsize = 8192;
 char   buffer[bsize];
 int   next_cluster = 0;
// fd = open(dev_name, O_RDONLY);
 fd = fopen(dev_name, "ro");
 if (fd <= 0) {
  //strcpy(volumename, "FAT\0");
//  close(fd);
//  fclose(fd);
  return false;
 }
// if (read(fd, buffer, bsize) != bsize) {
 if (fread(buffer, 1, bsize, fd) != bsize) {
  //strcpy(volumename, "FAT\0");
//  close(fd);
//  fclose(fd);
  return false;
 }
 // 0x16 & 0x17 is Sector per FAT
 f16_size = (buffer[0x16]&0xff) + (buffer[0x17]<<8);
 if (f16_size == 0) { // FAT32
  short reserved, sec_size;
  char clus, fats, attr;
  int f32_size, root_cluster, i, j;
  memcpy(volumename, buffer+0x47, len);
  // Sectors Per FAT
  f32_size = (buffer[0x24]&0xff) | (buffer[0x25]<<8) | (buffer[0x26]<<16) | (buffer[0x27]<<24);
  // Reserved Sectors
  reserved = (buffer[0xe]&0xff) | (buffer[0xf]<<8);
  // Bytes Per Sector
  sec_size = (buffer[0xb]&0xff) | (buffer[0xc]<<8);
  // Root Cluster
  root_cluster = (buffer[0x2c]&0xff) | (buffer[0x2d]<<8) | (buffer[0x2e]<<16) | (buffer[0x2f]<<24);
  clus = buffer[0xd];   // Sectors Per Cluster
  fats = buffer[0x10];  // No. of FATs
  next_cluster = root_cluster;
  // 0xFFFFFF7 is bad cluster, >= 0xFFFFFF8 is end Cluster, 0x0 if nod used cluster, Correct must 3 < x < 0xFFFFFF6
  while ( (next_cluster <= 0xFFFFFF6) && (next_cluster >= root_cluster) )  {
//   lseek(fd, (reserved+((fats)*(f32_size)))*(sec_size)+(root_cluster-2)*(clus)*(sec_size), SEEK_SET);
   fseeko(fd, (reserved+((fats)*(f32_size)))*(sec_size)+(next_cluster-2)*(clus)*(sec_size), SEEK_SET);
//   if (read(fd, buffer, bsize) != bsize) {
   if (fread(buffer, 1, bsize, fd) != bsize) {
    //strcpy(volumename, "FAT32\0");
    return false;
   }
   for (i = 0, j = -1; i < 256; i++) {
    attr = buffer[i*32+11]; // each entry 32 bytes, Byte 11 is Attribute
    if ((attr & 0x8) && ((attr & 0xf) != 0xf) && ((attr & 0xf0) == 0)) {
     char size;
     size = buffer[i*32+31]; // 28~31 is size
     if ( (size != 0) || ((int)*(buffer+i*32) == -27) )
      continue;
     j = i;
     break;
    }
   }
   if (j != -1)
    break;
   // Seek to FAT Table
   fseeko(fd, reserved*sec_size, SEEK_SET);
   if (fread(buffer, 1, bsize, fd) != bsize) {
    //strcpy(volumename, "FAT32\0");
    return false;
   }
   // Read FAT Table for root_cluster to search next Cluster
   next_cluster = (buffer[4*next_cluster]&0xff) | (buffer[4*next_cluster+1]<<8) | (buffer[4*next_cluster+2]<<16) | (buffer[4*next_cluster+3]<<24);
  }
  // Get Real Volume Label
  if ((j != -1) && ((int)*(buffer+j*32) != -27))
   memcpy(volumename, buffer+j*32, len);
  //else
   //strcpy(volumename, "FAT32\0");
 } else {    // FAT 16
  short reserved, sec_size;
  char fats, attr;
  int i, j;
  memcpy(volumename, buffer+0x2b, len);
  // Reserved Sectors
  reserved = (buffer[0xe]&0xff) | (buffer[0xf]<<8);
  // Bytes Per Sector
  sec_size = (buffer[0xb]&0xff) | (buffer[0xc]<<8);
  // No. of FATs
  fats = buffer[0x10];
//  lseek(fd, (reserved+((fats)*(f16_size)))*(sec_size), SEEK_SET);
  fseeko(fd, (reserved+((fats)*(f16_size)))*(sec_size), SEEK_SET);
//  if (read(fd, buffer, bsize) != bsize) {
  if (fread(buffer, 1, bsize, fd) != bsize) {
   //strcpy(volumename, "FAT16\0");
   return false;
  }
  for (i = 0, j = -1; i < 256; i++) {
   attr = buffer[i*32+11];
   if ((attr & 0x8) && ((attr & 0xf) != 0xf) && ((attr & 0xf0) == 0)) {
    char size;
    size = buffer[i*32+31];
    if ( (size != 0) || ((int)*(buffer+i*32) == -27) )
     continue;
    j = i;
    break;
   }
  }
  if ((j != -1) && ((int)*(buffer+j*32) != -27))
   memcpy(volumename, buffer+j*32, len);
  //else
   //strcpy(volumename, "FAT16\0");
 }
// close(fd);
 fclose(fd);
 return true;
}