#include #include #include #include #include #include #include #include "popen.h" #include "fstab.h" #include "fstab.m" static CONFIG_FILE f_partition ("/proc/partitions" ,help_nil,CONFIGF_PROBED|CONFIGF_OPTIONAL); /* describe one entry of the partition table */ PUBLIC PARTITION::PARTITION( const char *_dev, int _id, long _size, const char *_mpoint) { dev.setfrom (_dev); id = _id; size = _size; drive_letter = ' '; mpoint.setfrom (_mpoint); } PUBLIC PARTITION::~PARTITION() { } /* Set the drive letter DOS would normally associate with this partition. */ PUBLIC void PARTITION::setdosdrive(char letter) { drive_letter = letter; } /* Get the drive letter DOS would normally associate with this partition. */ PUBLIC char PARTITION::getdosdrive() { return drive_letter; } /* Return != 0 if the partition is a DOS one. */ PUBLIC int PARTITION::isdos() { return id == PARTITION_DOS_ID12 || id == PARTITION_DOS_ID16 || id == PARTITION_DOS_ID1632 || id == PARTITION_DOS_ID9532; } /* Return != 0 if the partition is a OS/2 one. */ PUBLIC int PARTITION::isos2() { return id == PARTITION_OS2_HPFS; } /* Return != if a partition is a swap. */ PUBLIC int PARTITION::isswap() { return id == PARTITION_LINUX_SWAP; } /* Return != if a partition is a native linux one (ext2). */ PUBLIC int PARTITION::islinux() { /* #Specification: fsconf / partition / linux native fsconf does not differentiate minix / xiafs / ext and ext2 partition. It assumes that a "Linux native" partition is always a ext2 one. */ return id == PARTITION_LINUX_NATIVE; } /* Return the path of the device controlling a partition. */ PUBLIC const char *PARTITION::getdevice() { return dev.get(); } /* Return the size (in block) of the partition */ PUBLIC long PARTITION::getsize() { return size; } /* Return the ID (in block) of the partition */ PUBLIC int PARTITION::getid() { return id; } PUBLIC void PARTITION::formatinfo(SSTRING &buf, bool show_mount) { char drive[3]; drive[0] = '\0'; if (drive_letter != ' '){ sprintf (drive,"%c:",drive_letter); } if (show_mount) buf.setfromf ("%s\t",mpoint.get()); buf.appendf ("%7ldM\t%-5s(%02x) %s" ,size/1024,getos() ,id,drive); } /* Return the name of the OS related to this partition if it is a filesystem. Return "" is unknown */ PUBLIC const char *PARTITION::getos() { char *ret = ""; if (isdos()){ ret = "Dos"; }else if (islinux()){ ret = "Linux"; }else if (isswap()){ ret = "Swap"; }else if (isos2()){ ret = "Os/2"; } return ret; } PUBLIC int PARTITION::readlabel(SSTRING &s) { int ret = -1; if (!label.is_empty()){ ret = 0; label.copy (s); }else if (islinux()){ POPEN pop ("e2label",getdevice()); if (pop.isok()){ while (1){ if (pop.wait(2)<=0){ break; } char buf[100]; if (pop.readout(buf,99)!=-1){ strip_end(buf); if (buf[0] != '\0'){ ret = 0; label.setfrom (buf); s.setfrom (buf); } break; } } } } return ret; } /* Read fdisk output to get the partition table */ PUBLIC int PARTITIONS::load() { /* #Specification: partition / probing Linuxconf is using the output from fdisk -l to learn about partition. With kernel 2.2, it can use /proc/partition to learn the list and sizes. But this lack the partition type. So it is using /proc/partition only to learn which disk do exist. Using /proc/ide seemed more appropriate, but the /proc/scsi counter part is not complete. Using /proc/scsi, one can't extract easily the list of disk and tell cdroms apart from disks and other devices. (or Am I missing something). If /proc/partitions is not available, then fdisk is called with an arbitrary list of device (/dev/hda /dev/hdb /dev/hdc ...). */ int ret = -1; MTAB mtab; SSTRING fdiskargs; { fdiskargs.setfrom ("-l"); FILE_CFG *fin = f_partition.fopen ("r"); bool found = false; if (fin != NULL){ char line[200]; while (fgets_strip(line,sizeof(line)-1,fin,NULL)!=NULL){ char buf[100],drive[100]; int size; if (sscanf (line,"%s %s %d %s",buf,buf,&size,drive)==4){ if (strncmp(drive,"hd",2)==0 || strncmp(drive,"rd",2)==0 || strncmp(drive,"sd",2)==0){ found = true; fdiskargs.appendf (" /dev/%s",drive); }else if(strncmp(drive,"md",2)==0){ char dev[10]; sprintf (dev,"/dev/%s",drive); // No partition for raid devices. We simply // assume they are linux file system on it FSTAB_ENTRY *e = mtab.locate_source(dev); const char *mpoint = ""; if (e != NULL) mpoint = e->getmpoint(); add (new PARTITION(dev,PARTITION_LINUX_NATIVE,size,mpoint)); } } } fclose (fin); } if (!found){ fdiskargs.append (" /dev/hda /dev/hdb /dev/hdc /dev/hdd" " /dev/sda /dev/sdb /dev/sdc /dev/sdd"); } } POPEN pop ("fdisk",fdiskargs.get()); // fprintf (stderr,"fdiskargs %s\n",fdiskargs.get()); if (pop.isok()){ int count=30; while (pop.wait(1)!=-1 && count-- > 0){ char buf[300]; while (pop.readout(buf,sizeof(buf)-1)!=-1){ if(strncmp(buf,"/dev/",5)==0){ char t[7][30]; int nb = sscanf (buf ,"%s %s %s %s %s %s %s" ,t[0],t[1],t[2],t[3],t[4],t[5] ,t[6]); // fprintf (stderr,"nb=%d :%s:\n",nb,t[0]); if (nb < 6){ xconf_error ( MSG_U(E_IVLDFDISK ,"Invalid output format for fdisk\n%s") ,buf); }else{ int offset = 5; if(strcmp(t[1],"*")==0) offset++; // Newer fdisk does not have a "Begin" column // in its report. So we may end in the "System" // column, so we step back if(!isxdigit(t[offset][0]) || strlen(t[offset]) > 2) offset--; int id; sscanf (t[offset],"%x",&id); FSTAB_ENTRY *e = mtab.locate_source(t[0]); const char *mpoint = ""; if (e != NULL) mpoint = e->getmpoint(); add (new PARTITION(t[0],id ,atoi(t[offset-1]),mpoint)); } } } while (pop.readerr(buf,sizeof(buf)-1)!=-1); } return 0; } setdosdrive(); return ret; } PUBLIC PARTITION *PARTITIONS::getitem(int no) const { return (PARTITION*)ARRAY::getitem(no); } /* Locate one partition in the table using its device path as key. Return NULL if not found. */ PUBLIC PARTITION *PARTITIONS::getitem(const char *device) const { PARTITION *ret = NULL; for (int i=0; igetdevice(),device)==0){ ret = p; break; } } return ret; } /* Assign DOS drive letter to DOS partition */ PROTECTED void PARTITIONS::setdosdrive() { /* #Specification: partitions / dos drive / how it works All this is very complicate. DOS allocate C on the first non extended DOS partition of the first drive. It then allocate D if possible on the first non extended DOS partition of the second drive. From there is get back to the first drive and allocate a letter to all DOS extended partition. After that it goes and finished the second hard drive. I have no idea what happen when you get more than four drives. DOS does not support it if I am right. Some info is needed here. */ char drive = 'C'; char minpart = '1'; char maxpart = '4'; for (int pass=0; pass<2; pass++){ for (int d='a'; d<='b'; d++){ for (int i=minpart; i<=maxpart; i++){ char buf[10]; sprintf (buf,"/dev/hd%c%c",d,i); PARTITION *p = getitem (buf); if (p != NULL && p->isdos()){ p->setdosdrive(drive); drive++; if (pass == 0) break; } } } minpart = '5'; maxpart = '8'; } } PUBLIC PARTITIONS::PARTITIONS() { } /* Probe the partition table of all devices. This take some time since fdisk is spawned to get it. So the result is kept from call to call. The returned object should not be deleted. */ PARTITIONS *partition_load() { /* #Specification: partition table / caching Parsing the partition table is often long on some station especially ones with SCSI adaptor. Given that it does not change during a linuxconf session, we load it only once in the partition_load() function and reuse the same result from call to call. There is an annoying case though. When building the treemenu this is taking much time, so in this case, we initialise an empty PARTITIONS object and return that. We use a flag to remember that the generated PARTITIONS object was never loaded so the next time partition_load is called, we know we have to reload the partition table. */ static PARTITIONS *parts = NULL; static bool delete_next = false; if (delete_next){ delete parts; parts = NULL; } if (parts == NULL){ parts = new PARTITIONS; if (dialog_mode != DIALOG_TREE){ parts->load(); delete_next = false; }else{ delete_next = true; } } return parts; } /* Locate a partition from its label */ const char *partition_findfromlabel(const char *label) { const char *ret = NULL; PARTITIONS *parts = partition_load(); for (int i=0; igetnb(); i++){ PARTITION *p = parts->getitem(i); SSTRING s; p->readlabel(s); // fprintf (stderr,"readlabel %d :%s: :%s: :%s:\n",p->islinux(),label,s.get(),p->getdevice()); if (p->readlabel(s)!=-1 && s.cmp(label)==0){ ret = p->getdevice(); break; } } return ret; } #ifdef TEST int main (int , char *[]) { PARTITIONS parts; for (int i=0; iisswap()){ type = "Swap"; }else if (p->isdos()){ type = "Dos"; }else if (p->islinux()){ type = "Linux"; } printf ("%s %ld %s\n",p->getdevice(),p->getsize() ,type); } return 0; } #endif