[comp.os.os9] ln, mv, link, and rename for os9/68k

blarson@skat.usc.edu (Bob Larson) (01/15/89)

Here is my implementation of these unix commands and routines for
os9/68k.  (Os9/6809 would need the missing routines like _ss_lock.)
Let me know if they screw up your hard disk, so I can fix them before
do it to mine.  :-) (This is NOT the version of mv I got from FHL,
that one did screw up my hard disk.)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	ln.doc
#	Makefile
#	ln.c
# This archive created: Sat Jan 14 10:56:42 1989
# By:	blarson
cat << \SHAR_EOF > ln.doc
/* Ln: create a hard link to a file (os9/68k)
 * by Robert A. Larson (blarson@skat.usc.edu)
 * This is placed in the public domain for all to enjoy.
 *
 ****** USE AT YOUR OWN RISK *****
 *
 * 01/14/89	Version 1.0
 *
 * Warning: this does potentialy dangerous writing to directories
 * and to the raw disk.  Although some attempt is made to lock out
 * other users from interfearing, I'm not sure the locking works.
 * Moving or links to directories should be used with EXTREME caution
 * if at all.  Note that moving a directory can make a directory tree
 * inaccessable, and 'deldir'ing a linked directory will delete its
 * contents and change the remaining links to normal files.
 */

This package implements the following for os9/68k:

	Ln and mv programs similar in function to their unix counterparts.

	link and rename functions similar to their unix counterparts.

The ln and mv commands use the -w option like the copy command to
specify a directory to link or move to rather than using the last file
name as their unix counterparts.  The -? option is the only other
option implemented.

Ln and the link subroutine create hard links.  Dcheck will report
errors on a RBF device with links, and dir should be modified to
display the link count.  Linked directories are not recomended, deldir
will cause problems with them, and .. will always point to the parent
of the original.  Ln and link are restricted to superuser, since the
raw disk is written to.

Mv and rename will move a file anywhere on an RBF device.  Unlike
unix, renaming to an existing name is an error, as is renaming to
another RBF device.  Renaming directories, other than without changing
parent directories, is restricted to superuser because it could
potentially cause a directory tree to become inaccessable.

The link and rename functions both take two character pointer
arguments, (old and new names) and return -1 and set errno on failure.
SHAR_EOF
cat << \SHAR_EOF > Makefile
ODIR = /dd/cmds
CFLAGS = -t=/r0
LFLAGS = -i -bg
./all: ln mv link.r
  touch all
ln: ln.r
  cc $(LFLAGS) ln.r -f=ln
mv: ln.r
  cc $(LFLAGS) ln.r -f=mv
ln.r: ln.c
link.r: ln.c
  cc $(CFLAGS) -r -dlink=link -fd=link ln.c
  touch link.r
  del link.r
  rename link link.r

SHAR_EOF
cat << \SHAR_EOF > ln.c
/* Ln: create a hard link to a file (os9/68k)
 * by Robert A. Larson (blarson@skat.usc.edu)
 * This is placed in the public domain for all to enjoy.
 *
 ****** USE AT YOUR OWN RISK *****
 *
 * 01/14/89	Version 1.0
 *
 * Warning: this does potentialy dangerous writing to directories
 * and to the raw disk.	 Although some attempt is made to lock out
 * other users from interfearing, I'm not sure the locking works.
 * Moving or links to directories should be used with EXTREME caution
 * if at all.  Note that moving a directory can make a directory tree
 * inaccessable, and 'deldir'ing a linked directory will delete its
 * contents and change the remaining links to normal files.
 */

#include <stdio.h>
#include <ctype.h>
#include <modes.h>
#include <direct.h>
#include <errno.h>

extern char *rindex();
extern int open(), close(), read(), write(), lseek();
extern int access(), getuid(), strcmp();
extern char *strcpy(), *strcat();
extern char *_prgname();
extern int _gs_devn(), _ss_lock();
extern long _gs_pos();

static int dncmp();		/* directory name comparison	*/
static int do_link();		/* does all the real work	*/

/* keys for the do_link subroutine */
#define DO_LINK		0
#define DO_RENAME	1
#define TO_DIR		128	/* orred with one of the above	*/

#ifndef link
main(argc, argv)
int argc;
char **argv;
{
    register int i;
    char *todir = NULL;
    int skip, what;

    for(i = 1; i < argc; i++) {
	if(argv[i][0]=='-') {
	    switch(argv[i][1]) {
		case '?':
		    usage();
		    exit(0);
		case 'w':
		    if(todir==NULL && argv[i][2]=='=') {
			todir = &argv[i][3];
			skip = i;
			continue;
		    }
		    /*FALLTHROUGH*/
		default:
		    usage();
		    exit(1);
	    }
	}
    }
    what = strcmp(argv[0],"ln")==0 ? DO_LINK : DO_RENAME;
    if(todir==NULL) {
	if(argc != 3) {
	    usage();
	    exit(1);
	}
	exit(do_link(argv[1], argv[2], what) < 0);
    }
    what |= TO_DIR;
    for(i=1; i < argc; i++) {
	if(i!=skip) {
	    if(do_link(argv[i], todir, what) < 0) exit(1);
	}
    }
    exit(0);
}

usage() {
    register char *prg = _prgname();

    fprintf(stderr,
"Usage: %s -?\n       %s <from> <to>\n       %s -w=<todir> <from> [<from>]...\n",
	prg, prg, prg);
}

#define problem(x)	fprintf x
#else
#define problem(x)	/* ignore */

link(old, new)
register char *old, *new;
{
    return do_link(old, new, DO_LINK);
}

rename(old, new)
register char *old, *new;
{
    return do_link(old, new, DO_RENAME);
}
#endif

do_link(old, new, action)
char *old, *new;
int action;
{
    char *olddir, *oldname, *newdir, *newname;
    int olddirp, newdirp;
    char olddevn[32], newdevn[32+2];
    struct dirent de;
    unsigned char fdl;
    int disk;
    long empty_slot;
    long newdir_addr;
    long entrydir_addr;
    int same;
    int i;
    long old_slot;
    char namebuf[256];
    char nn[128];

    if((oldname = rindex(old, '/')) == NULL) {
	olddir = ".";
	oldname = old;
    } else {
	olddir = namebuf;
	strncpy(namebuf, old, oldname - old);
	namebuf[oldname - old] = '\0';
	oldname++;
    }
    if(action & TO_DIR) {
	newdir = new;
	newname = oldname;
	action &= ~TO_DIR;
    } else {
	if((newname = rindex(new, '/')) == NULL) {
	    newdir = ".";
	    newname = new;
	} else {
	    newdir = namebuf;
	    if(olddir==namebuf) newdir += oldname - old;
	    strncpy(newdir, new, newname - new);
	    newdir[newname - new] = '\0';
	    newname++;
	}
    }
    if(_prsnam(newname) <= 0) {
	problem((stderr, "%s: Invalid file name \"%s\"\n", _prgname(), newname));
	return -1;
    }
    if((olddirp = open(olddir, S_IREAD | S_IWRITE | S_IFDIR)) < 0) {
	problem((stderr, "%s: Can't open old directory \"%s\"\n", _prgname(),
		olddir));
	return -1;
    }
    if((newdirp = open(newdir, S_IREAD | S_IWRITE | S_IFDIR)) < 0) {
	problem((stderr, "%s: Can't open new directory \"%s\"\n", _prgname(),
		newdir));
	close(olddirp);
	return -1;
    }
    _gs_devn(olddirp, olddevn);
    _gs_devn(newdirp, newdevn);
    if(strcmp(olddevn, newdevn) != 0) {
	problem((stderr, "%s: Not on same device \"%s\" \"%s\"\n",
		_prgname(), olddir, newdir));
	errno = E_DIFFER;
	close(olddirp);
	close(newdirp);
	return -1;
    }
    if((lseek(newdirp, (long)sizeof de, 0) < 0)				/* skip .. */
	    || (read(newdirp, (char *)&de, sizeof de) != sizeof de)) {	/* read . */
	problem((stderr, "%s: Problem reading \"%s\"\n", _prgname(), newdir));
	close(olddirp);
	close(newdirp);
	return -1;
    }
    newdir_addr = de.dir_addr;
    if((lseek(olddirp, (long)sizeof de, 0) < 0)				/* skip .. */
	    || (read(olddirp, (char *)&de, sizeof de) != sizeof de)) {	/* read . */
	problem((stderr, "%s: Problem reading \"%s\"\n", _prgname(), olddir));
	close(olddirp);
	close(newdirp);
	return -1;
    }
    if((same = (de.dir_addr == newdir_addr)) != 0) {
	/* link to same directory, scan only once */
	close(olddirp);
	olddirp = newdirp;
	/* rename to same name allowed, but not link */
	if(action == DO_LINK && dncmp(oldname, newname) == 0) {
	    problem((stderr, "%s: Can't link to same name \"%s\" \"%s\"\n",
		    _prgname(), oldname, newname));
	    errno = E_CEF;
	    close(newdirp);
	    return -1;
	}
	if(_ss_lock(newdirp, 0xfffffffff) < 0) {
	    problem((stderr, "%s: Could not lock directory \"%s\"\n",
		    _prgname(), newdir));
	    close(newdirp);
	    return -1;
	}
    }
    empty_slot = 0;
    for(;;) {
	if(read(olddirp, (char *)&de, sizeof de) != sizeof de) {
	    problem((stderr, "%s: Could not find \"%s\" in \"%s\"\n",
		    _prgname(), oldname, olddir));
	    errno = E_PNNF;
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(dncmp(de.dir_name, oldname) == 0) break;	/* found it */
	if(same) {
	    if(dncmp(de.dir_name, newname) == 0) {
		problem((stderr, "%s: Already exists \"%s/%s\"\n",
			_prgname(), newdir, newname));
		errno = E_CEF;
		close(newdirp);
		return -1;
	    }
	    if(empty_slot == 0 && de.dir_name[0] == '\0')
		empty_slot = _gs_pos(olddirp);
	}
    }
    if(action != DO_LINK) old_slot = _gs_pos(olddirp);
    entrydir_addr = de.dir_addr;
    if(!same) {
	if(_ss_lock(newdirp, 0xffffffff) < 0) {
	    problem((stderr, "%s: Can't lock directory \"%s\"\n", _prgname(),
		    newdir));
	    close(olddirp);
	    close(newdirp);
	    return -1;
	}
    }
    if(action != DO_LINK && same) empty_slot = old_slot;
    while(read(newdirp, (char *)&de, sizeof de) == sizeof de) {
	if(dncmp(de.dir_name, newname) == 0) {
	    problem((stderr, "%s: Already exists \"%s/%s\"\n", _prgname(),
		    newdir, newname));
	    errno = E_CEF;
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(empty_slot == 0 && de.dir_name[0] == '\0') {
	    empty_slot = _gs_pos(newdirp);
	}
    }
    strcpy(de.dir_name, newname);
    i = strlen(de.dir_name);
    de.dir_name[i-1] |= 0x80;
    while(i < sizeof de.dir_name) de.dir_name[i++] = '\0';
    de.dir_addr = entrydir_addr;
    if(empty_slot != 0) {
	if(lseek(newdirp, empty_slot - sizeof de, 0) < 0) {
	    problem((stderr, "%s: Can't position in \"%s\"\n", _prgname(),
		    newdir));
	    close(disk);
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
    }
    if(action == DO_LINK) {		/* link: increase the link count */
	newdevn[0] = '/';			/* form /<device>@ string */
	strcat(strcpy(newdevn+1, olddevn), "@");
	if((disk = open(newdevn, S_IREAD | S_IWRITE)) < 0) {
	    problem((stderr, "%s: Can't open \"%s\"\n", _prgname(), newdevn));
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(lseek(disk, (entrydir_addr<<8) +
		((char *)(&(((struct fildes *)0)->fd_link)) - (char *)0),
		0) < 0) {
	    problem((stderr, "%s: Can't seek to sector 0x%x on \"%s\"\n",
		    _prgname(), entrydir_addr, newdevn));
	    close(disk);
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(read(disk, (char *)&fdl, sizeof fdl) != sizeof fdl) {
	    problem((stderr, "%s: Can't read \"%s\"\n", _prgname(), newdevn));
	    close(disk);
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(fdl == 0) {
	    problem((stderr, "%s: Bad file descriptor \"%s/%s\"\n", _prgname(),
		    olddir, oldname));
	    errno = E_BMHP;		/* no appropriate error number */
	    close(disk);
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(++fdl == 0) {
	    problem((stderr, "%s: Too many links \"%s/%s\"\n", _prgname(),
		    olddir, oldname));
	    errno = E_BMHP;		/* no appropriate error number */
	    close(disk);
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(lseek(disk, -(long)sizeof fdl, 1) < 0) {
	    problem((stderr, "%s: Can't lseek on \"%s\"\n", _prgname(),
		    newdevn));
	    close(disk);
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(write(disk, (char *)&fdl, sizeof fdl) < 0) {
	    problem((stderr, "%s: Can't write to \"%s\"\n", _prgname(),
		    newdevn));
	    close(disk);
	    if(!same) close(olddirp);
	    close(newdirp);
	    return -1;
	}
    } else {
	strcpy(nn, newdir);
	strcat(nn, "/");
	strcat(nn, newname);
	/* Don't let non-superuser move directories, it is dangerous!
	 * (artificial restriction)
	 */
	if(!same && access(nn, 0) != 0 && getuid() != 0) {
	    problem((stderr, "%s: Directory moving can only be done by superuser\n",
		    _prgname()));
	    errno = E_FNA;
	    close(olddirp);
	    close(newdirp);
	    return -1;
	}
    }
    if(write(newdirp, (char *)&de, sizeof de) < 0) {
	problem((stderr, "%s: Can't write to \"%s\"\n", _prgname(), newdir));
	if(action==DO_LINK) close(disk);
	if(!same) close(olddirp);
	close(newdirp);
	return -1;
    }
    if(action != DO_LINK && !same) {		/* rename: delete old name */
	if((disk = open(nn, S_IREAD | S_IWRITE | S_IFDIR)) >= 0) {
	    /* change .. when moving directory */
	    if(lseek(disk, (long)sizeof de.dir_name, 0) < 0) {
		problem((stderr, "%s: can't position in \"%s\"\n",
			_prgname(), nn));
		close(disk);
		close(olddirp);
		close(newdirp);
		return -1;
	    }
	    if(write(disk, (char *)&newdir_addr, sizeof newdir_addr) < 0) {
		problem((stderr, "%s: Can't write to \"%s\"\n",
			_prgname(), nn));
		close(disk);
		close(olddirp);
		close(newdirp);
		return -1;
	    }
	    close(disk);
	}
	fdl = '\0';
	if(lseek(olddirp, old_slot - sizeof de, 0) < 0) {
	    problem((stderr, "%s: can't position in \"%s\"\n", _prgname(),
		    olddir));
	    close(olddirp);
	    close(newdirp);
	    return -1;
	}
	if(write(olddirp, (char *)&fdl, sizeof fdl) < 0) {
	    problem((stderr, "%s: can't write to \"%s\"\n", _prgname(),
		    olddir));
	    close(olddirp);
	    close(newdirp);
	    return -1;
	}
    }
    if(action == DO_LINK) close(disk);
    if(!same) close(olddirp);
    close(newdirp);
    return 0;
}

/* Compares two os9 file names, case independant.
 * The first argument may be terminated by setting the msb of
 * the last character or NUL terminated, the second must be NUL
 * termintated.	 Returns zero on same, non-zero on different.
 */
static int dncmp(cp1, cp2)
register char *cp1, *cp2;
{
    register int c1, c2;

    while((c1 = *cp1++) != 0) {
	if(c1 & 0x80) {
	    c1 &= 0x7f;
	    return ((c1 != (c2 = *cp2++)) &&
		    (isupper(c1) ? (tolower(c1) != c2) :
		    (!isupper(c2) || (tolower(c2) != c1)))) || *cp2;
	}
	if((c1 != (c2 = *cp2++)) && (isupper(c1) ? (tolower(c1) != c2) :
		(!isupper(c2) || (tolower(c2) != c1)))) return 1;
    }
    return *cp2;
}
SHAR_EOF
#	End of shell archive
exit 0




Bob Larson	Arpa: Blarson@Ecla.Usc.Edu	blarson@skat.usc.edu
Uucp: {sdcrdcf,cit-vax}!oberon!skat!blarson
Prime mailing list:	info-prime-request%ais1@ecla.usc.edu
			oberon!ais1!info-prime-request