From: brown@NCoast.ORG (Stan Brown)
Subject: Two volume labels per disk!
Organization: Oak Road Systems, Cleveland Ohio USA
Date: Fri, 18 Dec 1992 08:07:51 GMT
Followup-To: sender

In my relentless pursuit of quality in the FAQ list, I recently thought
I found a simpler answer to the question on reading and setting the
volume label: DOS function 69, introduced in DOS 4.0.  However, I was
surprised to discover that though I could change _a_ volume label using
that function, _the_ volume label displayed by theDIR, VOL, or LABEL
command was unchanged.

Following is the altered text of that portion of the FAQ, based on my
experiments.  I would welcome feedback, either suggestions for improving
clarity or (gulp!) factual corrections.  Please email, don't post, so
that I'm sure not to miss your comments.  Please make your comments as
specific as possible.

Q408. How can I read, create, change, or delete the volume label?

    In DOS 5.0 (and, I believe, in 4.0 as well), there are actually two
    volume labels: one, the traditional one, is an entry in the root
    directory of the disk and the other is in the boot record along with
    the serial number (see next Q).  The DIR and VOL commands report the
    traditional label; the LABEL command reports the traditional one but
    changes both of them.

    In DOS 4.0 and later, use INT 21 function 69 to access the boot
    record's serial number and volume label together; see the next Q.

    But more likely by "volume label" you mean the traditional one, the
    one that DIR and VOL display.  Though it's a directory entry in the
    root directory, you can't change it using the modern DOS file-access
    functions (3C, 41, 43); instead, use the old FCB-oriented directory
    functions.  Specifically, you need to allocate a 64-byte buffer and
    a 41- byte extended FCB (file control block).  Call INT 21 AH=1A to
    find out whether there is a volume label.  If there is, AL returns 0
    and you can change the label using DOS function 17 or delete it
    using DOS function 13.  If there's no volume label, function 1A will
    return FF and you can create a label via function 16.  Important
    points to notice are that ? wildcards are allowed but * are not;
    the volume label must be space filled not null-terminated.

    The following MSC 7.0 code worked for me in DOS 5.0; the functions
    it uses have been around since DOS 2.0.  The function parameter is 0
    for the current disk, 1 for a:, 2 for b:, etc.  It doesn't matter
    what your current directory is; these functions always search the
    root directory for volume labels.  (I didn't try to change the
    volume label of any networked drives.)

    // Requires DOS.H, STDIO.H, STRING.H
    void vollabel(unsigned char drivenum) {
        static unsigned char extfcb[41], dta[64], status, *newlabel;
        int chars_got = 0;
        #define DOS(buff,func) __asm { __asm mov dx,offset buff \
            __asm mov ax,seg buff  __asm push ds  __asm mov ds,ax \
            __asm mov ah,func  __asm int 21h  __asm pop ds \
            __asm mov status,al }
        #define getlabel(buff,prompt) newlabel = buff;  \
            memset(newlabel,' ',11);  printf(prompt);   \
            scanf("%11[^\n]%n", newlabel, &chars_got);  \
            if (chars_got < 11) newlabel[chars_got] = ' ';

        // Set up the 64-byte transfer area used by function 1A.
        DOS(dta, 1Ah)
        // Set up an extended FCB and search for the volume label.
        memset(extfcb, 0, sizeof extfcb);
        extfcb[0] = 0xFF;             // denotes extended FCB
        extfcb[6] = 8;                // volume-label attribute bit
        extfcb[7] = drivenum;         // 1=A, 2=B, etc.; 0=current drive
        memset(&extfcb[8], '?', 11);  // wildcard *.*
        DOS(extfcb,11h)
        if (status == 0) {            // DTA contains volume label's FCB
            printf("volume label is %11.11s\n", &dta[8]);
            getlabel(&dta[0x18], "new label (\"delete\" to delete): ");
            if (chars_got == 0)
                printf("label not changed\n");
            else if (strncmp(newlabel,"delete     ",11) == 0) {
                DOS(dta,13h)
                printf(status ? "label failed\n" : "label deleted\n");
            }
            else {                    // user wants to change label
                DOS(dta,17h)
                printf(status ? "label failed\n" : "label changed\n");
            }
        }
        else {                        // no volume label was found
            printf("disk has no volume label.\n");
            getlabel(&extfcb[8], "new label (<Enter> for none): ");
            if (chars_got > 0) {
                DOS(extfcb,16h)
                printf(status ? "label failed\n" : "label created\n");
            }
        }
    }   // end function vollabel

Q409. How can I get the disk serial number?

    Use INT 21.  AX=6900 gets the serial number; AX=6901 sets it.  See
    Ralf Brown's interrupt list, or page 496 of the July 1992 {PC
    Magazine}, for details.

    This function also gets and sets the volume label, but it's the
    volume label in the boot record, not the volume label that a DIR
    command displays.  See the preceding Q.

    I tested the following MSC 7.0 code under DOS 5.0.  The function
    parameter is 0 for the current disk, 1 for a:, 2 for b:, etc.

    // Requires DOS.H, STDIO.H, STRING.H
    void labserial(unsigned char drivenum) {
        static unsigned char bpb[0x19], *label = &bpb[6];
        static unsigned *serial = (unsigned *)(&bpb[2]);
        int chars_got = 0;
        #define DOS(func) __asm { __asm mov bl,drivenum \
          __asm mov dx,offset bpb __asm mov ax,seg bpb __asm push ds \
          __asm mov ds,ax __asm mov ax,func __asm int 21h __asm pop ds }

        // Read the disk information.
        DOS(6900h)
        printf("serial number: %04X-%04X\n", serial[1], serial[0]);
        printf("volume label: [%-.11s]\n", label);

        // Get the new label and serial number.
        memset(label, ' ', 11);
        printf("new label (\"NO NAME\" to delete): ");
        scanf("%11[^\n]%n", label, &chars_got);
        if (chars_got == 0)
            printf("label not changed\n");
        else {
            if (chars_got < 11)
                label[chars_got] = ' ';
            printf("new serial number (8 hex digits): ");
            scanf("%lx", (unsigned long *)serial);
            DOS(6901h)
        }
    }   // end function labserial
-- 
Stan Brown, Oak Road Systems                      brown@Ncoast.ORG
"There are no good guys.  There's bad guys and worse guys.  The best one
can hope for is to defend the bad against the worse."
                                 -- {Almost History} by Christopher Bram
Newsgroups: comp.os.msdos.programmer