[comp.sys.atari.st] Volume Labels under TOS 1.4

apratt@atari.UUCP (Allan Pratt) (10/13/88)

After careful consideration, and some input from a couple of people
(including credit to Kbad for the ultimate solution), I have reworked
volume label handling in TOS 1.4.

Originally, I dropped volume labels entirely: if you tried to Fcreate
a file with the volume bit set, you'd get a regular file.  Then somebody
complained about that, and I changed it so the Fcreate would fail.

However, I became convinced (thanks to Michael Fischer, among others)
that this was bad: the only way to create volume labels would be by
using Rwabs, and it is impossible to do that safely: if any of your
parents has an open file on the drive, calling the "cause media change"
procedure I provided would be a disaster.

So I decided to make them work at least as well as before, and hopefully
better.  See for yourself.

					-- Allan Pratt

======= CUT HERE FOR APPLICATION NOTE REGARDING VOLUME LABELS ========

10/11/88: volume labels

The behavior of GEMDOS with respect to volume labels has been changed. 

First, here's the new behavior:

	1.  The only available operations on volume labels are Fsfirst
(if bit 0x08 is set, you'll see 'em) and Fcreate. 

	2.  Fcreate(file,0x08) will create a volume label IF the 'file'
ends up in the root directory of a device.  Otherwise you get EACCDN. 

	3.  Once you Fcreate a volume label, you should Fclose the
handle immediately.  This is not enforced, but any data you write to the
handle is lost forever. 

	4.  Before the new label is created, any label on that device is
deleted.  (Well, specifically, the first file in the root directory of
the device with type == 0x08, if any, is deleted.)

	5.  You can create a volume label with the same name as an
existing file, or vice versa.  Both will coexist just fine on the disk.

	6.  You can't Fdelete, Frename, or Fattrib a volume label. 

	7.  You can't use Fattrib to make a file into a volume label. 

	8.  You can't remove a volume label.  (But see #4: you can
rename one simply by creating one with the new name.)

In the old GEMDOS, people sometimes did this to create a new label:

	Fsetdta(&dta);
	if (!Fsfirst("\\*.*",0x08)) {
		/* a label already exists: delete it. */
		fd = Fcreate(dta.name,0);
		Fclose(fd);
		Fdelete(dta.name);
	}
	fd = Fcreate("\\mylabel",0x08);
	Fclose(fd);

This sequence works because Fcreate'ing a file with the same name as
the label would remove the label.  Then you could delete the file.
Having done that, you know the last Fcreate will be the only volume
label on the disk.

Well, this sequence will still work.  The clause which checks for and
deletes an existing label is not needed, but harmless.  The last Fcreate
will replace the old volume label with the new one. 

In the old GEMDOS, the above algorithm will wipe out an existing file
which has the same name as the NEW label.  In the new one, it will wipe
out a file with the same name as the OLD label.  The advantage of the
new GEMDOS is that you don't need the first clause at all: just Fcreate
the new label, and you won't wipe out any files at all:

	fd = Fcreate("\\mylabel",0x08);
	Fclose(fd);

This works whether you already have a label or not, and whether you have
a file called mylabel or not, and it doesn't wipe out that file if there
is one. 

Of course, since programs should run on any system with any ROMs,
and you should do error checking, this code will work best:

/*
 * Program to change a disk's volume label.
 *
 * Should be run from the volume's root directory.  The single command-line
 * argument should be the new label to create.  Any old label is removed.
 * Works for all ROM versions, both pre- and post-TOS 1.4.
 * 
 * This code is for Alcyon, but should compile under almost all ST compilers.
 *
 * Written by Allan Pratt, of Atari Corp., October 12, 1988; released
 * to the general public to be freely used and copied with no restrictions.
 */

#include <osbind.h>

int romvers;
long mklabel();

main(argc,argv)
int argc;
char *argv[];
{
	long oldssp;
	int *sysbase;
	char newlabel[12];
	char pathbuf[128];

	oldssp = Super(0L);
	sysbase = *(int **)0x4f2;
	romvers = *(sysbase+1);
	Super(oldssp);

	Dgetpath(pathbuf,0);

	if (*pathbuf != '\0' ||		/* must run from the root directory */
	    argc != 2 ||		/* expect exactly one argument */
	    argv[1][1] == ':' ||	/* which must not have a drive spec */
	    index(argv[1],"\\")) {	/* ...or a path spec */

		Cconws("Usage: label <newlabel>\r\n");
		Cconws("Must be run from the root directory,\r\n");
		Cconws("and <newlabel> must be a simple file name.\r\n");
		Pterm(1);
	}

	/* copy the argument because Fsfirst will overwrite it! */
	strcpy(newlabel,argv[1]);

	if (mklabel(newlabel)) {
		Cconws("Failed.\r\n");
		Pterm(1);
	}
	Pterm0();
}

/*
 * mklabel: this procedure creates a new volume label on the current drive,
 * replacing any existing label.  The current directory must be the root
 * of the drive you want to create the label on, and the argument must
 * not contain a drive or path specifier.
 *
 * (Those restrictions are not demanded by Fcreate; they're demanded
 * by the section of code inside "if (romvers < 0x0104)" because that
 * way Fsfirst("*.*",0x08) will refer to the same drive & directory
 * as the label you're trying to create.)
 */

struct _dta {
	char reserved[21];
	char attr;
	int time;
	int date;
	long size;
	char name[14];	/* null terminated */
} *dta;

long mklabel(newlabel)
register char *newlabel;
{
	register long err;
	register int fd;
	extern int romvers;

	/* If earlier than TOS 1.4, delete the old label. */
	/* Assume that romvers has been set elsewhere.    */

	if (romvers < 0x0104) {
		dta = Fgetdta();
		err = Fsfirst("*.*",0x08);
		if (err == 0) {
			/* An old label exists: create a normal file with */
			/* the same name, then delete that new file.	  */

			if ((fd = err = Fcreate(dta->name,0)) < 0)
				return err;
			Fclose(fd);
			Fdelete(dta->name);
		}
	}
	if ((fd = err = Fcreate(newlabel,0x08)) < 0)
		return err;
	err = Fclose(fd);
	return err;
}