SQZDIR
utzoo!decvax!duke!harpo!eagle!mhuxt!mhuxi!mhuxj!mhuxv!burl!sb1!sb6!jca
utzoo!decvax!duke!harpo!eagle!mhuxt!mhuxi!mhuxj!mhuxv!burl!sb1!sb6!jca
Mon Jan 17 00:21:45 AEST 1983
/*
** program = [ sqzdir ]
** author = [ Jackson C. allen ]
** compile = [ cc -O -s -i sqzdir.c ]
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <signal.h>
/*
** These defines and include contorl certain options
** to be complied in and or if the system this is
** run on supports certain features.
**
** LOCK:
** If process is to be locked in core (not swapped), system
** has to support this feature.
**
** ASAP:
** If process is to have a very high priority.
**
*/
#define LOCK 1
#define ASAP 1
#ifdef LOCK
#include <sys/lock.h>
int plock();
#endif
/*********************************************************************/
#define PROG "sqzdir"
#define VPRINT (void)fprintf
#define VSPRINT (void)sprintf
#define VCPY (void)strcpy
#define VNCPY (void)strncpy
#define VCAT (void)strcat
#define MAXPATH 256 /* MAX path length */
#define ROOT 0 /* ROOT uid */
#define ROOTNODE 2 /* ROOT inode for a file system */
#define MAXARG 100 /* MAX number of directories on command line */
#define MAXSIG 18 /* MAX singnals to ignore, etc. */
#define MAXENTRY 1000 /* MAX active entries in a directory */
#define SAME (0)
#define PASS (0)
#define FAIL (-1)
#define OFF (0)
#define ON (1)
#define DIR (1)
#define OTHER (2)
extern int errno;
extern char *sys_errlist[];
ino_t cwd_ino;
dev_t cwd_dev;
struct FINFO {
int type;
long atime;
char name[DIRSIZ+1];
};
struct FINFO finfo[MAXENTRY];
int (*signal())();
int sigcatch();
int (*sigsave[MAXSIG])();
int cmp();
int argcnt;
int sigflag = OFF;
int Dflag = OFF; /* DEBUG FLAG */
int rflag = OFF; /* recursive flag */
int vflag = OFF; /* verbose flag */
int lflag = OFF; /* list flag */
char *strcpy();
char *strcat();
char *strncpy();
char *argptr[MAXARG];
main(agc,arg,env)
int agc;
char **arg;
char **env;
{
register int i;
register int n;
struct stat statb;
if(env); /* MAKE LINT HAPPY FOR NOW */
/*
** Make sure the program is being executed by root or
** that it's effected uid is root.
*/
if(getuid() != ROOT && geteuid() != ROOT) {
VPRINT(stdout,"%s: Must be root or program set uid to root\n",
PROG);
exit(1);
}
if(stat(".",&statb) == FAIL) {
VPRINT(stdout,"%s: INIT() Can't stat current directory\n",
PROG);
syserr();
exit(1);
}
cwd_ino = statb.st_ino;
cwd_dev = statb.st_dev;
/*
** If there are not any arguments we can't do much.
*/
if(agc == 1) {
usage();
exit(1);
}
/*
** Well there was some arguments so we will have to look
** at them and see if any are flag options.
*/
for(i=1;i < agc;i++) {
if(arg[i][0] == '-') {
for(n=1;n < strlen(arg[i]);n++) {
switch(arg[i][n]) {
case 'v':
vflag = ON;
break;
case 'r':
rflag = ON;
break;
case 'D':
Dflag = ON;
vflag = ON;
break;
case 'l':
lflag = ON;
vflag = ON;
break;
default: /* UNKNOWN FLAG */
usage();
exit(1);
}
}
}
else {
/*
** If there is room put the argumnet on the list.
*/
if(argcnt < MAXARG) {
/*
** Validate that the argument is a directory
** and it is not the current directory.
*/
valid(arg[i]);
argptr[argcnt] = arg[i];
argcnt++;
}
else {
VPRINT(stdout,"%s: Argument count overflow\n",
PROG);
exit(1);
}
}
}
#ifdef ASAP
/*
** Make priority so that this will get done ASAP
** if lflag is OFF.
*/
if(lflag == OFF)
(void)nice(-100);
#endif
#ifdef LOCK
/*
** Lock process in memory so we can't be swaped out, only
** if the lflag is OFF. This will help keep some other program
** from creating new files in a directory slot we have already
** found to be empty. This will only be of much good if we are
** running this on an active system on a directory that
** is of public access (/usr/tmp, /usr/spool, ...). It
** mite be a little over kill and not realy do much
** good on top of that. Anyway lock both text and data
** protions of the program in memory.
*/
if(lflag == OFF)
(void)plock(PROCLOCK);
#endif
/*
** Everything is OK so far so let's squeeze some directoies.
*/
for(i=0;i < argcnt;i++) {
if(vflag == ON)
VPRINT(stdout,"01 '%s':\n",argptr[i]);
/*
** Ignore signals because some of the work done
** by sqzdir will really screw up the file system
** if inrrupted before it completes.
*/
setsigs(OFF);
if(sqzdir(argptr[i],1) == FAIL)
exit(1);
setsigs(ON);
/*
** If sigflag is on then we caught some signal
** so we will just exit.
*/
if(sigflag == ON) {
VPRINT(stdout,"\n");
exit(1);
}
}
exit(0);
}
sqzdir(dir,level)
register char *dir;
int level;
{
register int i;
register int n;
FILE *ifd;
ushort d_mode;
ushort d_uid;
ushort d_gid;
struct stat statb;
struct direct dirb;
long offset;
int sqz;
int dcnt;
int slots;
int vaccant;
char dpath[MAXPATH];
char oldp[MAXPATH];
char newp[MAXPATH];
char fname[MAXPATH];
char tmpdir[MAXPATH];
char name[16];
if(sigflag == ON)
return(PASS);
if(stat(dir,&statb) == FAIL) {
d_stat("SQZDIR",dir);
return(FAIL);
}
d_mode = (statb.st_mode & 07777);
d_uid = statb.st_uid;
d_gid = statb.st_gid;
sqz = ON;
/*
** If this is the current working directory then we will not
** sort or squeeze this directory, but if there are other
** directories in this directory then they may be squeezed.
** Or if the directory is a mounted file system and the rflag
** is not ON then just don't do anything.
*/
if((statb.st_ino == cwd_ino && statb.st_dev == cwd_dev) ||
(statb.st_ino == ROOTNODE))
sqz = OFF;
if(sqz == OFF && rflag == OFF && lflag == OFF)
return(PASS);
if((ifd=fopen(dir,"r")) == NULL) {
d_open("SQZDIR","open",dir);
return(FAIL);
}
n = 0;
dcnt = 0;
slots = 0;
vaccant = 0;
while(fread((char *)&dirb,sizeof(dirb),1,ifd) == 1) {
slots++; /* count the slots */
if(dirb.d_ino == 0) {
vaccant++; /* count the vaccant ones */
continue;
}
VNCPY(name,"",sizeof(name));
VNCPY(name,dirb.d_name,sizeof(dirb.d_name));
if(strcmp(name,".") == SAME || strcmp(name,"..") == SAME)
continue;
VSPRINT(fname,"%s/%s",dir,name);
if(stat(fname,&statb) == FAIL) {
de_stat("SQZDIR",fname);
(void)fclose(ifd);
return(FAIL);
}
/*
** If the entry is a directory then finfo[n].type will
** be set to DIR else it will be set to OTHER.
** finfo[n].atime will be set to the last access
** time for the entry and finally finfo[n].name will
** contain the entry name. This will be sorted in
** reverse order which will cause all the directories
** to be at the top and in order of last accessed first
** and then the others in last accessed first.
*/
if((statb.st_mode & S_IFMT) == S_IFDIR) {
finfo[n].type = DIR;
dcnt++;
}
else
finfo[n].type = OTHER;
finfo[n].atime = statb.st_atime;
VCPY(finfo[n].name,name);
n++;
/*
** There is a limit to the number of active entries in a
** directory that will fit in memory and have room to
** recursively call yourself.
*/
if(n >= MAXENTRY) {
VPRINT(stdout,"\n\n%s: SQZDIR() Too many directory",
PROG);
VPRINT(stdout,"entries, MAX=(%d)\n",MAXENTRY);
return(FAIL);
}
/*
** If there is a mounted file system in this directory
** or our current working directory then we can't squeeze
** this directory, but we have to go through this in case
** rflag is set and there are some other directories.
*/
if((statb.st_ino == cwd_ino && statb.st_dev == cwd_dev) ||
(statb.st_ino == ROOTNODE))
sqz = OFF;
}
(void)fclose(ifd);
if(sigflag == ON)
return(PASS);
if(vflag == ON) {
for(i=1;i < level;i++)
VPRINT(stdout," ");
VPRINT(stdout," Entries: %.3d Directroies: ",slots);
VPRINT(stdout,"%.3d Unused: %.3d ",dcnt,vaccant);
}
if(sqz == OFF && rflag == OFF) {
if(vflag)
VPRINT(stdout,"\n\n");
return(PASS);
}
/*
** If there are not any vaccant slots and none of
** the slots are directories there is nothing else to do.
*/
if(vaccant == 0 && dcnt == 0) {
if(vflag)
VPRINT(stdout,"\n\n");
return(PASS);
}
/*
** If sqz is not ON or the lflag in ON then don't waste time
** sorting, makeing a temporary directory, fixing mode, fixing
** ownership, and squeezing the directory.
*/
if(sqz == ON && lflag == OFF) {
qsort((char *)finfo,(unsigned int)n,sizeof(finfo[0]),cmp);
if(sigflag == ON) {
if(vflag)
VPRINT(stdout,"\n\n");
return(PASS);
}
path(dir,dpath);
/*
** We have to try and create a temporary directory
** that does not already exist. It's name will be
** SQZppppp.llnn where ppppp = process id,
** ll = level down in sub-directories, nn = next
** number incase the rest match a directroy or file
** that already exist for some unknow reason.
*/
i = 0;
do {
if(strlen(dpath))
VSPRINT(tmpdir,"%s/SQZ%.5d.%.2d%.2d",
dpath,getpid(),level,i);
else
VSPRINT(tmpdir,"SQZ%.5d.%.2d%.2d",
getpid(),level,i);
i++;
}while(access(tmpdir,0) == PASS);
if(mkdir(tmpdir) == FAIL)
return(FAIL);
/*
** We have to make sure it belongs to the same owner
** and group and has the same mode.
*/
if(chown(tmpdir,(int)d_uid,(int)d_gid) == FAIL) {
VPRINT(stdout,"\n\n%s: SQZDIR() Can't chown ",PROG);
VPRINT(stdout,"and group for '%s'\n",tmpdir);
syserr();
(void)rmdir(tmpdir,OFF);
return(FAIL);
}
if(chmod(tmpdir,(int)d_mode) == FAIL) {
VPRINT(stdout,"\n\n%s: SQZDIR() Can't chmod ",PROG);
VPRINT(stdout,"for directory '%s'\n",tmpdir);
syserr();
(void)rmdir(tmpdir,OFF);
return(FAIL);
}
if(sigflag == ON) {
if(vflag)
VPRINT(stdout,"\n\n");
(void)rmdir(tmpdir,OFF);
return(PASS);
}
if(vflag)
VPRINT(stdout,"+");
/*
** Now that the temporary directory is there we can move
** all the other directories and files to it.
*/
for(i=0;i < n;i++) {
VSPRINT(oldp,"%s/%s",dir,finfo[i].name);
VSPRINT(newp,"%s/%s",tmpdir,finfo[i].name);
if(finfo[i].type == DIR)
if(mvdir(oldp,newp) == FAIL)
return(FAIL);
if(finfo[i].type == OTHER)
if(mvfile(oldp,newp) == FAIL)
return(FAIL);
}
/*
** Now that everything has been moved we can
** remove the old directory and rename the temporary
** directory the same as the old directory.
*/
if(rmdir(dir,ON) == FAIL)
return(FAIL);
if(link(tmpdir,dir) == FAIL) {
d_link("SQZDIR",tmpdir,dir);
corrupt();
return(FAIL);
}
if(unlink(tmpdir) == FAIL) {
d_unlink("SQZDIR",tmpdir);
corrupt();
return(FAIL);
}
if(sigflag == ON) {
if(vflag)
VPRINT(stdout,"\n\n");
return(PASS);
}
}
if(vflag)
VPRINT(stdout,"\n\n");
/*
** If the rflag is ON and there are sub-directories in this
** directory then walk througt them and squeeze them and ...
*/
if(rflag == ON && dcnt != 0) {
if((ifd=fopen(dir,"r")) == NULL) {
d_open("SQZDIR","reopen",dir);
return(FAIL);
}
while(fread((char *)&dirb,sizeof(dirb),1,ifd) == 1) {
if(dirb.d_ino == 0)
continue;
VNCPY(name,"",sizeof(name));
VNCPY(name,dirb.d_name,sizeof(dirb.d_name));
if(strcmp(name,".") == SAME ||
strcmp(name,"..") == SAME)
continue;
VSPRINT(fname,"%s/%s",dir,name);
/*
** We have to stat the entry to find out if
** it is a directory. We can't use the table
** we built earlier because it is global and
** may have changed.
*/
if(stat(fname,&statb) == FAIL) {
de_stat("SQZDIR",fname);
(void)fclose(ifd);
return(FAIL);
}
if((statb.st_mode & S_IFMT) == S_IFDIR) {
if(vflag == ON) {
for(i=0;i < level;i++)
VPRINT(stdout," ");
VPRINT(stdout,"%.2d '%s':\n",
(level + 1),fname);
}
/*
** We first find out which slot we are
** at in this directory and then close
** it so we don't get too many files
** opened at once. When we get back we
** will have to reopen it and seek to
** the proper slot.
*/
offset = ftell(ifd);
(void)fclose(ifd);
if(sqzdir(fname,(level + 1)) == FAIL)
return(FAIL);
if(sigflag == ON)
return(PASS);
if((ifd=fopen(dir,"r")) == NULL) {
d_open("SQZDIR","reopen",dir);
return(FAIL);
}
if(fseek(ifd,offset,0) == FAIL) {
VPRINT(stdout,"\n\n%s: SQZDIR() ",
PROG);
VPRINT(stdout,"Can't seek to (%ld) ",
offset);
VPRINT(stdout,"on directory '%s'\n",
dir);
syserr();
(void)fclose(ifd);
return(FAIL);
}
}
/*
** If lflag is OFF then we have squeezed the directory
** and all sub-directories are at the top so if
** it is not a directory in this slot then there
** is nothing else to do
*/
else if(lflag == OFF)
break;
}
(void)fclose(ifd);
}
return(PASS);
}
valid(dir)
register char *dir;
{
struct stat statb;
char dname[32];
if(access(dir,0) == FAIL) {
VPRINT(stdout,"%s: '%s' does not exist\n",PROG,dir);
exit(1);
}
basename(dir,dname);
if(strlen(dname) == 0) {
VPRINT(stdout,"%s: NULL directory name\n",PROG);
exit(1);
}
if(stat(dir,&statb) == FAIL) {
VPRINT(stdout,"%s: VALID() Can't stat '%s'\n",PROG);
syserr();
exit(1);
}
if((statb.st_mode & S_IFMT) != S_IFDIR) {
VPRINT(stdout,"%s: '%s' is not a directory\n",PROG,dir);
exit(1);
}
if(cwd_ino == statb.st_ino && cwd_dev == statb.st_dev) {
VPRINT(stdout,"%s: Current directory not allowed\n",PROG);
exit(1);
}
}
/*
** Basename is used to find the last part of
** a path and put it in dname.
*/
basename(dir,dname)
char *dir;
char *dname;
{
register char *p1;
register char *p2;
p1 = dir;
p2 = dir;
while(*p1)
p1++;
while(p1 != p2 && *p1 != '/')
p1--;
if(*p1 == '/')
p1++;
VCPY(dname,p1);
}
/*
** Path is used to find the path leading up
** to the last part and puts it in dpath.
*/
path(dir,dpath)
char *dir;
char *dpath;
{
register char *p1;
register char *p2;
VCPY(dpath,dir);
p1 = dpath;
p2 = dpath;
while(*p1)
p1++;
while(p1 != p2) {
if(*p1 == '/')
break;
p1--;
}
*p1 = '\0';
}
/*
** Rmdir is used to remove all the entries in a directory
** including '.' and '..' and then the directory itself.
** Note that the entries really are not delete since they
** should have a link into another directory. All this really
** does is correct link counts for some other directory and
** then delete the directory that was passed.
*/
rmdir(dir,flag)
register char *dir;
register int flag;
{
register FILE *tfd;
struct direct dirb;
char fname[MAXPATH];
char name[MAXPATH];
/*
** First open the directory and look at each slot
** and delete each entry except "." and ".." which
** are deleted last thing.
*/
if((tfd=fopen(dir,"r")) == NULL) {
if(flag == ON)
d_open("RMDIR","open",dir);
return(FAIL);
}
while(fread((char *)&dirb,sizeof(dirb),1,tfd) == 1) {
if(dirb.d_ino == 0)
continue;
VNCPY(name,"",sizeof(name));
VNCPY(name,dirb.d_name,sizeof(dirb.d_name));
if(strcmp(name,".") == SAME || strcmp(name,"..") == SAME)
continue;
VSPRINT(fname,"%s/%s",dir,name);
if(unlink(fname) == FAIL) {
(void)fclose(tfd);
if(flag == ON) {
VPRINT(stdout,"\n\n%s: RMDIR() Can't ",PROG);
VPRINT(stdout,"unlink directory entry '%s'\n",
fname);
syserr();
}
return(FAIL);
}
}
(void)fclose(tfd);
VSPRINT(fname,"%s/..",dir);
if(unlink(fname) == FAIL) {
if(flag == ON) {
d_unlink("RMDIR",fname);
corrupt();
}
return(FAIL);
}
VSPRINT(fname,"%s/.",dir);
if(unlink(fname) == FAIL) {
if(flag == ON) {
d_unlink("RMDIR",fname);
corrupt();
}
return(FAIL);
}
if(unlink(dir) == FAIL) {
if(flag == ON) {
d_unlink("RMDIR",dir);
corrupt();
}
return(FAIL);
}
return(PASS);
}
usage()
{
VPRINT(stdout,"%s: Usage = %s [-r] [-l] [-v] directory ...\n",
PROG,PROG);
}
/*
** NOTE: The bulk of this code was taken form the mkdir command in
** (/usr/src/cmd/mkdir.c). Error messages were added.
*/
mkdir(dir)
register char *dir;
{
register int i;
register int slash;
char pname[MAXPATH];
char dname[MAXPATH];
slash = 0;
pname[0] = '\0';
for(i=0;dir[i];++i)
if(dir[i] == '/')
slash = (i + 1);
if(slash)
VNCPY(pname,dir,slash);
VCPY((pname+slash),".");
if((mknod(dir,040777,0)) == FAIL) {
VPRINT(stdout,"\n\n%s: MKDIR() Can't mknod ",PROG);
VPRINT(stdout,"for directory '%s'\n",dir);
syserr();
return(FAIL);
}
VCPY(dname,dir);
VCAT(dname, "/.");
if((link(dir,dname)) == FAIL) {
d_link("MKDIR",dir,dname);
(void)unlink(dir);
return(FAIL);
}
VCAT(dname, ".");
if((link(pname,dname)) == FAIL) {
d_link("MKDIR",pname,dname);
dname[strlen(dname)] = '\0';
(void)unlink(dname);
(void)unlink(dir);
return(FAIL);
}
return(PASS);
}
/*
** Mvdir is used to mv a directory form the old directory
** to the new temporary directory. Care must be taken to
** make sure we get the link count correct in all the
** directories concerned.
*/
mvdir(oldp,newp)
register char *oldp;
register char *newp;
{
char d1[MAXPATH];
char d2[MAXPATH];
/*
** First link the old directory to the new directory.
*/
if(link(oldp,newp) == FAIL) {
d_link("MVDIR",oldp,newp);
corrupt();
return(FAIL);
}
/*
** Then unlink the old entry.
*/
if(unlink(oldp) == FAIL) {
d_unlink("MVDIR",oldp);
corrupt();
return(FAIL);
}
/*
** Now correct some of the link counts and associate the
** new directory with it's correct parrent.
*/
VSPRINT(d1,"%s/..",newp);
/*
** Do away with the link to the old parrent.
*/
if(unlink(d1) == FAIL) {
d_unlink("MVDIR",d1);
corrupt();
return(FAIL);
}
path(newp,d2);
/*
** Associate it with it's new parrent.
*/
if(link(d2,d1) == FAIL) {
d_link("MVDIR",d2,d1);
corrupt();
return(FAIL);
}
return(PASS);
}
/*
** Mvfile is used to mv a file from the old directory
** to it's new home. It is a lot more simple than the
** mvdir subroutine.
*/
mvfile(oldp,newp)
register char *oldp;
register char *newp;
{
if(link(oldp,newp) == FAIL) {
f_link("MVFILE",oldp,newp);
corrupt();
return(FAIL);
}
if(unlink(oldp) == FAIL) {
f_unlink("MVFILE",oldp);
corrupt();
return(FAIL);
}
return(PASS);
}
corrupt()
{
VPRINT(stdout,"\tFile system may be corrupted\n");
}
syserr()
{
VPRINT(stdout,"\t%s\n",sys_errlist[errno]);
}
setsigs(flag)
register int flag;
{
register int i;
switch(flag) {
case OFF:
for(i=0;i < MAXSIG;i++)
sigsave[i] = signal(i,sigcatch);
break;
case ON:
for(i=0;i < MAXSIG;i++)
(void)signal(i,sigsave[i]);
break;
}
}
sigcatch(sig)
register int sig;
{
(void)signal(sig,sigcatch);
sigflag = ON;
}
/*
** Cmp is called by qsort() to compare 2 entries in
** finfo table.
*/
cmp(e1,e2)
register struct FINFO *e1;
register struct FINFO *e2;
{
register int r;
r = 0;
if(e1->type < e2->type)
r--;
if(e1->type > e2->type)
r++;
/*
** If types are the same then compare the access times
** highest number means the entry has been accessed
** the latest.
*/
if(r == 0) {
if(e1->atime < e2->atime)
r++;
if(e1->atime > e2->atime)
r--;
}
return(r);
}
d_link(sub,dir1,dir2)
register char *sub;
register char *dir1;
register char *dir2;
{
VPRINT(stdout,"\n\n%s: %s() Can't link directory '%s' to '%s`,\n",
PROG,sub,dir1,dir2);
syserr();
}
d_unlink(sub,dir)
register char *sub;
register char *dir;
{
VPRINT(stdout,"\n\n%s: %s() Can't unlink directory '%s',\n",
PROG,sub,dir);
syserr();
}
d_stat(sub,dir)
register char *sub;
register char *dir;
{
VPRINT(stdout,"\n\n%s: %s() Can't stat directory '%s',\n",
PROG,sub,dir);
syserr();
}
de_stat(sub,dir)
register char *sub;
register char *dir;
{
VPRINT(stdout,"\n\n%s: %s() Can't stat directory entry '%s',\n",
PROG,sub,dir);
syserr();
}
d_open(sub,type,dir)
register char *sub;
register char *type;
register char *dir;
{
VPRINT(stdout,"\n\n%s: %s() Can't %s directory '%s',",
PROG,sub,type,dir);
syserr();
}
f_link(sub,file1,file2)
register char *sub;
register char *file1;
register char *file2;
{
VPRINT(stdout,"\n\n%s: %s() Can't link file '%s' to '%s',\n",
PROG,sub,file1,file2);
syserr();
}
f_unlink(sub,file)
register char *sub;
register char *file;
{
VPRINT(stdout,"\n\n%s: %s() Can't unlink file '%s',\n",
PROG,sub,file);
syserr();
}
More information about the Comp.sources.unix
mailing list