[comp.sys.amiga.tech] Finding disks

chris@genly.UUCP (Chris Hind Genly) (11/19/90)

I posted a message a few days ago asking how to determine what disk
devices are on a system.  This is the kind of problem you need to
solve if you're writing a file requester and want to put up buttons
with disk device names on them.  I received one answer.  I was pointed
towards the fish disk with the info command.  I thought I'd post the
results since I imagine there are readers that would be interested.

The info replacement walked through the dos device list looking for a
device with an associated task.  There is a comment in the info source
to the effect that only disks will have an associated task.  I don't
think this is correct.  It appears that disk devices have a task, but
other devices do too.  The info replacement shows all the disks on my
system plus null:.  The Commodore info command doesn't show null:.

I have a good solution now.  The magic I needed came from the info
replacement command.  It showed me how to suppress requesters.  The
requester is suppressed by setting the window pointer in the process
structure to 0.

Once a device is found with a non-zero task entry, it can be tested to
see if it is really a disk device by trying to lock it.  If you can
lock it, its a disk.  If you can't it still might be a disk.  If its
df0: for example and there is no disk in df0: I don't want a requester
to pop up, so I suppress them.  If there's no disk, the lock operation
will fail.  If the device is a disk device the lock will fail with an
error such as: ERROR_NO_DISK.  This tells me the device is a disk
device. 

Here is my current solution.  You are free to do with the code as you wish.

static
void
FindDiskNames(
    char *DiskNames[],
    int   DiskNamesMax
)
{
    extern struct DosLibrary *DOSBase;
    struct RootNode *RootNode;
    struct DosInfo *DosInfo;
    struct DosList *Device, *Devices;
    struct Process *Process;
    char *DiskName, *t;
    APTR OldWindow;
    char *Name;
    int ADisk;
    BPTR lk;
    int i;
    
    DiskNames[0] = 0;
    
    if (DOSBase == 0) return;
    
    RootNode = (struct RootNode *)DOSBase->dl_Root;
    DosInfo  = (struct DosInfo *)BADDR(RootNode->rn_Info);
    Devices  = (struct DosList *)BADDR(DosInfo->di_DevInfo);
    
    for(Device = Devices;
        Device;
        Device = (struct DosList *)BADDR(Device->dol_Next))
    {
        /* Look for a device, not a volume */
        if (Device->dol_Type != 0)
            continue;
            
        /* A disk device has a task associated with it */
        if (Device->dol_Task == 0)
            continue;

        /* Get the device name */
        Name =  (char *)BADDR(Device->dol_Name);
        Name++;
        
        /* Make a copy with a colon at the end */
        DiskName = smalloc(strlen(Name)+2);
        strcpy(DiskName, Name);
        strcat(DiskName, ":");
        
        /* Disable requesters in case there's no disk present */
        Process = (struct Process *) FindTask(NULL);
        OldWindow = Process->pr_WindowPtr;
        Process->pr_WindowPtr = (APTR) -1;
        
        /* Attempt to create a lock to verify that its a disk */
        lk = Lock(DiskName, ACCESS_READ);
        if (lk) UnLock(lk);
        ADisk  = lk != 0 
              || IoErr() == ERROR_NOT_A_DOS_DISK	
              || IoErr() == ERROR_NO_DISK;
              
        /* Allow requesters */
        Process->pr_WindowPtr = OldWindow;
        
        /* If its a disk, add it to the list of disk names */
        if (ADisk) {
            for(i=0; i<DiskNamesMax; ++i) {
                if (DiskNames[i] == 0) break;
                if (stricmp(DiskName, DiskNames[i]) < 0) break;
            }
            for(; i<DiskNamesMax; ++i) {
                t = DiskNames[i];
                if (t == 0 && i == DiskNamesMax-1) {
                    sfree(DiskName);
                    break;
                } else {
                    DiskNames[i] = DiskName;
                    if (DiskName == 0) break;
                    DiskName = t;
                }
            }
        } else
            sfree(DiskName);
    }
}

sfree(), sstrdup(), and smalloc() are strict versions of free(), strdup(),
and malloc().  In addition to performing their normal function they check the
integrity of the memory pool.


       *                        *                        *
                                                            \|/
               *           _______                         --O--
                      ____/ KC1VP \____        *            /|\
 *             ______/  (203) 389-8680 \______
        ______/   Eggplant Software Tools     \________
 ______/  95 Fountain Terr., New Haven, CT, USA, 06515 \_______
/ Chris Hind Genly    chris@genly.uucp   uunet!hsi!genly!chris \
----------------------------------------------------------------

ridder@elvira.enet.dec.com (Hans Ridder) (11/20/90)

In article <chris.11172988@genly.UUCP> chris@genly.UUCP (Chris Hind Genly) writes:
>I posted a message a few days ago asking how to determine what disk
>devices are on a system.  This is the kind of problem you need to
>solve if you're writing a file requester and want to put up buttons
>with disk device names on them.

>The info replacement walked through the dos device list looking for a
>device with an associated task.

>Once a device is found with a non-zero task entry, it can be tested to
>see if it is really a disk device by trying to lock it.  If you can
>lock it, its a disk.

I don't think it's a good idea to write code which depends on side
effects like this.  (Unless it's written down somewhere that only "disk
devices" know how to Lock() -- I've never seen it.)  As you pointed out,
the Info replacment depended on the perceived effect that only devices
have tasks, which later turned out to be wrong.  There's always a danger
in depending on undocumented side effects.

IMHO, requesters should list *volumes*, not *devices*.  Volume names are
(or can be) much more descriptive to users than are names like "DH0" or
"DF1".  If device names are used, the user might have to click on every
button to find a file, if the right disk is even in a drive.  Devices
(i.e. disk drives) don't have files on them, volumes do.

Notice that the Workbench uses volume names, not devices names.  It'll
be much easier for the user to find the desired file if the names shown
on the requester are consistent with the names he/she just saw on the
Workbench screen.

It's also much easier to find volumes in the device list.

-hans
------------------------------------------------------------------------
  Hans-Gabriel Ridder			Digital Equipment Corporation
  ridder@elvira.enet.dec.com		Customer Support Center
  ...decwrl!elvira.enet!ridder		Colorado Springs, CO

ggk@tirith.UUCP (Gregory Kritsch) (11/21/20)

chris@genly.UUCP (Chris Hind Genly) writes:
>Once a device is found with a non-zero task entry, it can be tested to
>see if it is really a disk device by trying to lock it.  If you can
>lock it, its a disk.  If you can't it still might be a disk.  If its
>df0: for example and there is no disk in df0: I don't want a requester
>to pop up, so I suppress them.  If there's no disk, the lock operation
>will fail.  If the device is a disk device the lock will fail with an
>error such as: ERROR_NO_DISK.  This tells me the device is a disk
>device. 

>Here is my current solution.  You are free to do with the code as you wish.

>static
>void
>FindDiskNames(
>    char *DiskNames[],
>    int   DiskNamesMax
>)
>{

>        /* Look for a device, not a volume */
>        if (Device->dol_Type != 0)
>            continue;

Hmm, so you assume that there is a device for every volume, which is not
neccesarily the case.  Case in point, my network file system from last
year.  It created only volume nodes for network volumes, the idea of
trying to dynamically allocate device names scared me, and didn't seem
very logical either (there was an ND0: entry used basically to find the
handler, it only supported certain custom packets though). 

I wasn't aware at that time that it would break a good number of file
requesters, as well as some Workbench replacement type programs that
assumed device entries as well.

>        /* A disk device has a task associated with it */
>        if (Device->dol_Task == 0)
>            continue;

>        /* Get the device name */
>        Name =  (char *)BADDR(Device->dol_Name);
>        Name++;
>        
>        /* Make a copy with a colon at the end */
>        DiskName = smalloc(strlen(Name)+2);
>        strcpy(DiskName, Name);
>        strcat(DiskName, ":");
>        
>        /* Disable requesters in case there's no disk present */
>        Process = (struct Process *) FindTask(NULL);
>        OldWindow = Process->pr_WindowPtr;
>        Process->pr_WindowPtr = (APTR) -1;
>        
>        /* Attempt to create a lock to verify that its a disk */
>        lk = Lock(DiskName, ACCESS_READ);
>        if (lk) UnLock(lk);
>        ADisk  = lk != 0 
>              || IoErr() == ERROR_NOT_A_DOS_DISK	
>              || IoErr() == ERROR_NO_DISK;

Two thoughts here: It might be more prudent to send the packet directly
to the file process, rather than go through all the mumbo jumbo
converting the BSTR and adding the colon and so on.  This also has the
advantage of not having to play with pr_WindowPtr, since the dos
requestor generator mechanism can't get in the way.

Also, the test "IoErr() != ERROR_ACTION_NOT_KNOWN" might be better - all
disk handlers will understand ACTION_LOCK, but almost all non-disk
handlers won't understand it (and should return ERROR_ACTION_NOT_KNOWN).

>/ Chris Hind Genly    chris@genly.uucp   uunet!hsi!genly!chris \
--
  Gregory Kritsch                          | University of Waterloo
    Fido:  1:221/208.11110  [1:163/109.30] | 1A Computer Engineering
    OCUG:  ggk@tirith.ocug.on.ca           |----------------------------
    UUCP:  ggk@tirith.UUCP                 | The University doesn't get
           ...!watmath!xenitec!tirith!ggk  | a chance to censor me!