首先看看/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;
}