џh џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџрџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџэA@“Œ:! эA0“Œ:! ЄЧ“Œ:!Є “Œ:!Єq)“Œ:!Єћ“Œ:!Є—“Œ:! !Єѕ“Œ:!"Є—“Œ:!#$Єк“Œ:!%Є“Œ:!&Є “Œ:!'()Єn*“Œ:!*+,-./02ЄЌ “Œ:!678Є‹“Œ:!9:;<=>?Є™ “Œ:!@ABCЄЉ“Œ:!DEFGHIJLЄ“Œ:!MNOPQRЄф'“Œ:!STUVWXY[ЄР“Œ:!^_`abcdfЄ“Œ:!ghijklmЄЬ$“Œ:!nopqrstvЄ"“Œ:!yz{|}~Єы“Œ:!ƒ„…†‡ˆЄЁ“Œ:!‰ŠЄy/“Œ:!‹ŒŽ‘“Є“Œ:!˜™š›œЄЩ"“Œ:!žŸ ЁЂЃЄІЄ“Œ:!ЈЉЊЋЌЄђ “Œ:!­ЎЏЄ“Œ:!АБВГДЕЄЛ“Œ:!ЖЗИЙКЛМОџA0“Œ:!ПЄ"“Œ:!РСТУФџA0“Œ:!ХЄ<“Œ:!ЦЧШЩџAP“Œ:!ЪЄ’“Œ:!ЫЬЭЮЯаЄЧ“Œ:!бЄ“Œ:!вџA€“Œ:!гЄ“Œ:!дэh“Œ:!ежЄN•“Œ:!зийклмнпџA`“Œ:!>ЄV“Œ:!?Єg“Œ:!@Єп“Œ:!AЄЄ“Œ:!BџA`“Œ:!CЄV“Œ:!DЄg“Œ:!EЄо“Œ:!FЄУ“Œ:!GџAа“Œ:!HЄЉ“Œ:!IЄЌ“Œ:!JЄ“Œ:!KLЄb“Œ:!MЄœ“Œ:!NЄz“Œ:!OPЄщ“Œ:!QЄT“Œ:!RЄЪ“Œ:!SЄ;“Œ:!TЄQ“Œ:!UGъ!...fs)lib...type.hsuper.hpath.cparam.hinode.hglo.h fproc.h file.h dev.h const.h cache.cbuf.hdevice.cfiledes.cinode.clink.cmain.cmisc.cmount.copen.cpipe.cprotect.cputc.cread.cstadir.csuper.ctable.ctime.cutility.c write.c!MINIX#PCIX%C86/* Type definitions local to the File System. */ typedef struct { /* directory entry */ inode_nr d_inum; /* inode number */ char d_name[NAME_SIZE]; /* character string */ } dir_struct; /* Declaration of the disk inode used in rw_inode(). */ typedef struct { /* disk inode. Memory inode is in "inotab.h" */ mask_bits i_mode; /* file type, protection, etc. */ uid i_uid; /* user id of the file's owner */ file_pos i_size; /* current file size in bytes */ real_time i_modtime; /* when was file data last changed */ gid i_gid; /* group number */ links i_nlinks; /* how many links to this file */ zone_nr i_zone[NR_ZONE_NUMS]; /* block nums for direct, ind, and dbl ind */ } d_inode; /* Super block table. The root file system and every mounted file system * has an entry here. The entry holds information about the sizes of the bit * maps and inodes. The s_ninodes field gives the number of inodes available * for files and directories, including the root directory. Inode 0 is * on the disk, but not used. Thus s_ninodes = 4 means that 5 bits will be * used in the bit map, bit 0, which is always 1 and not used, and bits 1-4 * for files and directories. The disk layout is: * * Item # blocks * boot block 1 * super block 1 * inode map s_imap_blocks * zone map s_zmap_blocks * inodes (s_ninodes + 1 + INODES_PER_BLOCK - 1)/INODES_PER_BLOCK * unused whatever is needed to fill out the current zone * data zones (s_nzones - s_firstdatazone) << s_log_zone_size * * A super_block slot is free if s_dev == NO_DEV. */ EXTERN struct super_block { inode_nr s_ninodes; /* # usable inodes on the minor device */ zone_nr s_nzones; /* total device size, including bit maps etc */ unshort s_imap_blocks; /* # of blocks used by inode bit map */ unshort s_zmap_blocks; /* # of blocks used by zone bit map */ zone_nr s_firstdatazone; /* number of first data zone */ short int s_log_zone_size; /* log2 of blocks/zone */ file_pos s_max_size; /* maximum file size on this device */ int s_magic; /* magic number to recognize super-blocks */ /* The following items are only used when the super_block is in memory. */ struct buf *s_imap[I_MAP_SLOTS]; /* pointers to the in-core inode bit map */ struct buf *s_zmap[ZMAP_SLOTS]; /* pointers to the in-core zone bit map */ dev_nr s_dev; /* whose super block is this? */ struct inode *s_isup; /* inode for root dir of mounted file sys */ struct inode *s_imount; /* inode mounted on */ real_time s_time; /* time of last update */ char s_rd_only; /* set to 1 iff file sys mounted read only */ char s_dirt; /* CLEAN or DIRTY */ } super_block[NR_SUPERS]; #define NIL_SUPER (struct super_block *) 0 /* This file contains the procedures that look up path names in the directory * system and determine the inode number that goes with a given path name. * * The entry points into this file are * eat_path: the 'main' routine of the path-to-inode conversion mechanism * last_dir: find the final directory on a given path * advance: parse one component of a path name * search_dir: search a directory for a string and return its inode number */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "super.h" /*===========================================================================* * eat_path * *===========================================================================*/ PUBLIC struct inode *eat_path(path) char *path; /* the path name to be parsed */ { /* Parse the path 'path' and put its inode in the inode table. If not * possible, return NIL_INODE as function value and an error code in 'err_code'. */ register struct inode *ldip, *rip; char string[NAME_SIZE]; /* hold 1 path component name here */ extern struct inode *last_dir(), *advance(); /* First open the path down to the final directory. */ if ( (ldip = last_dir(path, string)) == NIL_INODE) return(NIL_INODE); /* we couldn't open final directory */ /* The path consisting only of "/" is a special case, check for it. */ if (string[0] == '\0') return(ldip); /* Get final component of the path. */ rip = advance(ldip, string); put_inode(ldip); return(rip); } /*===========================================================================* * last_dir * *===========================================================================*/ PUBLIC struct inode *last_dir(path, string) char *path; /* the path name to be parsed */ char string[NAME_SIZE]; /* the final component is returned here */ { /* Given a path, 'path', located in the fs address space, parse it as * far as the last directory, fetch the inode for the last directory into * the inode table, and return a pointer to the inode. In * addition, return the final component of the path in 'string'. * If the last directory can't be opened, return NIL_INODE and * the reason for failure in 'err_code'. */ register struct inode *rip; register char *new_name; register struct inode *new_ip; extern struct inode *advance(); extern char *get_name(); /* Is the path absolute or relative? Initialize 'rip' accordingly. */ rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir); dup_inode(rip); /* inode will be returned with put_inode */ /* Scan the path component by component. */ while (TRUE) { /* Extract one component. */ if ( (new_name = get_name(path, string)) == (char*) 0) { put_inode(rip); /* bad path in user space */ return(NIL_INODE); } if (*new_name == '\0') return(rip); /* normal exit */ /* There is more path. Keep parsing. */ new_ip = advance(rip, string); put_inode(rip); /* rip either obsolete or irrelevant */ if (new_ip == NIL_INODE) return(NIL_INODE); /* The call to advance() succeeded. Fetch next component. */ path = new_name; rip = new_ip; } } /*===========================================================================* * get_name * *===========================================================================*/ PRIVATE char *get_name(old_name, string) char *old_name; /* path name to parse */ char string[NAME_SIZE]; /* component extracted from 'old_name' */ { /* Given a pointer to a path name in fs space, 'old_name', copy the next * component to 'string' and pad with zeros. A pointer to that part of * the name as yet unparsed is returned. Roughly speaking, * 'get_name' = 'old_name' - 'string'. * * This routine follows the standard convention that /usr/ast, /usr//ast, * //usr///ast and /usr/ast/ are all equivalent. */ register int c; register char *np, *rnp; np = string; /* 'np' points to current position */ rnp = old_name; /* 'rnp' points to unparsed string */ while ( (c = *rnp) == '/') rnp++; /* skip leading slashes */ /* Copy the unparsed path, 'old_name', to the array, 'string'. */ while ( rnp < &user_path[MAX_PATH] && c != '/' && c != '\0') { if (np < &string[NAME_SIZE]) *np++ = c; c = *++rnp; /* advance to next character */ } /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */ while (c == '/' && rnp < &user_path[MAX_PATH]) c = *++rnp; /* Pad the component name out to NAME_SIZE chars, using 0 as filler. */ while (np < &string[NAME_SIZE]) *np++ = '\0'; if (rnp >= &user_path[MAX_PATH]) { err_code = E_LONG_STRING; return((char *) 0); } return(rnp); } /*===========================================================================* * advance * *===========================================================================*/ PUBLIC struct inode *advance(dirp, string) struct inode *dirp; /* inode for directory to be searched */ char string[NAME_SIZE]; /* component name to look for */ { /* Given a directory and a component of a path, look up the component in * the directory, find the inode, open it, and return a pointer to its inode * slot. If it can't be done, return NIL_INODE. */ register struct inode *rip; struct inode *rip2; register struct super_block *sp; int r; dev_nr mnt_dev; inode_nr numb; extern struct inode *get_inode(); /* If 'string' is empty, yield same inode straight away. */ if (string[0] == '\0') return(get_inode(dirp->i_dev, dirp->i_num)); /* If 'string' is not present in the directory, signal error. */ if ( (r = search_dir(dirp, string, &numb, LOOK_UP)) != OK) { err_code = r; return(NIL_INODE); } /* The component has been found in the directory. Get inode. */ if ( (rip = get_inode(dirp->i_dev, numb)) == NIL_INODE) return(NIL_INODE); if (rip->i_num == ROOT_INODE) if (dirp->i_num == ROOT_INODE) { if (string[1] == '.') { for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++) { if (sp->s_dev == rip->i_dev) { /* Release the root inode. Replace by the * inode mounted on. */ put_inode(rip); mnt_dev = sp->s_imount->i_dev; rip2 = get_inode(mnt_dev, sp->s_imount->i_num); rip = advance(rip2, string); put_inode(rip2); break; } } } } /* See if the inode is mounted on. If so, switch to root directory of the * mounted file system. The super_block provides the linkage between the * inode mounted on and the root directory of the mounted file system. */ while (rip->i_mount == I_MOUNT) { /* The inode is indeed mounted on. */ for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) { if (sp->s_imount == rip) { /* Release the inode mounted on. Replace by the * inode of the root inode of the mounted device. */ put_inode(rip); rip = get_inode(sp->s_dev, ROOT_INODE); break; } } } return(rip); /* return pointer to inode's component */ } /*===========================================================================* * search_dir * *===========================================================================*/ PUBLIC int search_dir(ldir_ptr, string, numb, flag) register struct inode *ldir_ptr; /* ptr to inode for dir to search */ char string[NAME_SIZE]; /* component to search for */ inode_nr *numb; /* pointer to inode number */ int flag; /* LOOK_UP, ENTER, or DELETE */ { /* This function searches the directory whose inode is pointed to by 'ldip': * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb'; * if (flag == ENTER) enter 'string' in the directory with inode # '*numb'; * if (flag == DELETE) delete 'string' from the directory; */ register dir_struct *dp; register struct buf *bp; register int r; mask_bits bits; file_pos pos; unsigned new_slots, old_slots; block_nr b; int e_hit; extern struct buf *get_block(), *new_block(); extern block_nr read_map(); extern real_time clock_time(); /* If 'ldir_ptr' is not a pointer to a searchable dir inode, error. */ if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) return(ENOTDIR); bits = (flag == LOOK_UP ? X_BIT : W_BIT|X_BIT); if ( (r = forbidden(ldir_ptr, bits, 0)) != OK) return(r); /* Step through the directory one block at a time. */ old_slots = ldir_ptr->i_size/DIR_ENTRY_SIZE; new_slots = 0; e_hit = FALSE; for (pos = 0; pos < ldir_ptr->i_size; pos += BLOCK_SIZE) { b = read_map(ldir_ptr, pos); /* get block number */ /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */ bp = get_block(ldir_ptr->i_dev, b, NORMAL); /* get a dir block */ /* Search a directory block. */ for (dp = &bp->b_dir[0]; dp < &bp->b_dir[NR_DIR_ENTRIES]; dp++) { if (++new_slots > old_slots) { /* not found, but room left */ if (flag == ENTER) e_hit = TRUE; break; } if (flag != ENTER && dp->d_inum != 0 && cmp_string(dp->d_name, string, NAME_SIZE)) { /* LOOK_UP or DELETE found what it wanted. */ if (flag == DELETE) { dp->d_inum = 0; /* erase entry */ bp->b_dirt = DIRTY; ldir_ptr->i_modtime = clock_time(); } else *numb = dp->d_inum; /* 'flag' is LOOK_UP */ put_block(bp, DIRECTORY_BLOCK); return(OK); } /* Check for free slot for the benefit of ENTER. */ if (flag == ENTER && dp->d_inum == 0) { e_hit = TRUE; /* we found a free slot */ break; } } /* The whole block has been searched or ENTER has a free slot. */ if (e_hit) break; /* e_hit set if ENTER can be performed now */ put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */ } /* The whole directory has now been searched. */ if (flag != ENTER) return(ENOENT); /* This call is for ENTER. If no free slot has been found so far, try to * extend directory. */ if (e_hit == FALSE) { /* directory is full and no room left in last block */ new_slots ++; /* increase directory size by 1 entry */ if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */ if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NIL_BUF) return(err_code); dp = &bp->b_dir[0]; } /* 'bp' now points to a directory block with space. 'dp' points to slot. */ copy(dp->d_name, string, NAME_SIZE); dp->d_inum = *numb; bp->b_dirt = DIRTY; put_block(bp, DIRECTORY_BLOCK); ldir_ptr->i_modtime = clock_time(); ldir_ptr->i_dirt = DIRTY; if (new_slots > old_slots) ldir_ptr->i_size = (file_pos) new_slots * DIR_ENTRY_SIZE; return(OK); } /* The following names are synonyms for the variables in the input message. */ #define acc_time m.m2_l1 #define addr m.m1_i3 #define buffer m.m1_p1 #define cd_flag m.m1_i2 #define child m.m1_i2 #define co_mode m.m1_i1 #define eff_grp_id m.m1_i3 #define eff_user_id m.m1_i3 #define erki m.m1_p1 #define fd m.m1_i1 #define fd2 m.m1_i2 #define ioflags m.m1_i3 #define group m.m1_i3 #define real_grp_id m.m1_i2 #define ls_fd m.m2_i1 #define mk_mode m.m1_i2 #define mode m.m3_i2 #define name m.m3_p1 #define name1 m.m1_p1 #define name2 m.m1_p2 #define name_length m.m3_i1 #define name1_length m.m1_i1 #define name2_length m.m1_i2 #define nbytes m.m1_i2 #define offset m.m2_l1 #define owner m.m1_i2 #define parent m.m1_i1 #define pathname m.m3_ca1 #define pro m.m1_i1 #define rd_only m.m1_i3 #define real_user_id m.m1_i2 #define request m.m1_i2 #define sig m.m1_i2 #define slot1 m.m1_i1 #define tp m.m2_l1 #define update_time m.m2_l2 #define utime_file m.m2_p1 #define utime_length m.m2_i1 #define whence m.m2_i2 /* The following names are synonyms for the variables in the output message. */ #define reply_type m1.m_type #define reply_l1 m1.m2_l1 #define reply_i1 m1.m1_i1 #define reply_i2 m1.m1_i2 #define reply_t1 m1.m4_l1 #define reply_t2 m1.m4_l2 #define reply_t3 m1.m4_l3 #define reply_t4 m1.m4_l4 /* Inode table. This table holds inodes that are currently in use. In some * cases they have been opened by an open() or creat() system call, in other * cases the file system itself needs the inode for one reason or another, * such as to search a directory for a path name. * The first part of the struct holds fields that are present on the * disk; the second part holds fields not present on the disk. * The disk inode part is also declared in "type.h" as 'd_inode'. */ EXTERN struct inode { unshort i_mode; /* file type, protection, etc. */ uid i_uid; /* user id of the file's owner */ file_pos i_size; /* current file size in bytes */ real_time i_modtime; /* when was file data last changed */ gid i_gid; /* group number */ links i_nlinks; /* how many links to this file */ zone_nr i_zone[NR_ZONE_NUMS]; /* zone numbers for direct, ind, and dbl ind */ /* The following items are not present on the disk. */ dev_nr i_dev; /* which device is the inode on */ inode_nr i_num; /* inode number on its (minor) device */ short int i_count; /* # times inode used; 0 means slot is free */ char i_dirt; /* CLEAN or DIRTY */ char i_pipe; /* set to I_PIPE if pipe */ char i_mount; /* this bit is set if file mounted on */ char i_seek; /* set on LSEEK, cleared on READ/WRITE */ } inode[NR_INODES]; #define NIL_INODE (struct inode *) 0 /* indicates absence of inode slot */ /* Field values. Note that CLEAN and DIRTY are defined in "const.h" */ #define NO_PIPE 0 /* i_pipe is NO_PIPE if inode is not a pipe */ #define I_PIPE 1 /* i_pipe is I_PIPE if inode is a pipe */ #define NO_MOUNT 0 /* i_mount is NO_MOUNT if file not mounted on */ #define I_MOUNT 1 /* i_mount is I_MOUNT if file mounted on */ #define NO_SEEK 0 /* i_seek = NO_SEEK if last op was not SEEK */ #define ISEEK 1 /* i_seek = ISEEK if last op was SEEK */ /* File System global variables */ EXTERN struct fproc *fp; /* pointer to caller's fproc struct */ EXTERN int super_user; /* 1 if caller is super_user, else 0 */ EXTERN int dont_reply; /* normally 0; set to 1 to inhibit reply */ EXTERN int susp_count; /* number of procs suspended on pipe */ EXTERN int reviving; /* number of pipe processes to be revived */ EXTERN file_pos rdahedpos; /* position to read ahead */ EXTERN struct inode *rdahed_inode; /* pointer to inode to read ahead */ /* The parameters of the call are kept here. */ EXTERN message m; /* the input message itself */ EXTERN message m1; /* the output message used for reply */ EXTERN int who; /* caller's proc number */ EXTERN int fs_call; /* system call number */ EXTERN char user_path[MAX_PATH];/* storage for user path name */ /* The following variables are used for returning results to the caller. */ EXTERN int err_code; /* temporary storage for error number */ EXTERN char fstack[FS_STACK_BYTES]; /* the File System's stack. */ /* This is the per-process information. A slot is reserved for each potential * process. Thus NR_PROCS must be the same as in the kernel. It is not possible * or even necessary to tell when a slot is free here. */ EXTERN struct fproc { mask_bits fp_umask; /* mask set by umask system call */ struct inode *fp_workdir; /* pointer to working directory's inode */ struct inode *fp_rootdir; /* pointer to current root dir (see chroot) */ struct filp *fp_filp[NR_FDS]; /* the file descriptor table */ uid fp_realuid; /* real user id */ uid fp_effuid; /* effective user id */ gid fp_realgid; /* real group id */ gid fp_effgid; /* effective group id */ dev_nr fs_tty; /* major/minor of controlling tty */ int fp_fd; /* place to save fd if rd/wr can't finish */ char *fp_buffer; /* place to save buffer if rd/wr can't finish */ int fp_nbytes; /* place to save bytes if rd/wr can't finish */ char fp_suspended; /* set to indicate process hanging */ char fp_revived; /* set to indicate process being revived */ char fp_task; /* which task is proc suspended on */ } fproc[NR_PROCS]; /* Field values. */ #define NOT_SUSPENDED 0 /* process is not suspended on pipe or task */ #define SUSPENDED 1 /* process is suspended on pipe or task */ #define NOT_REVIVING 0 /* process is not being revived */ #define REVIVING 1 /* process is being revived from suspension */ /* This is the filp table. It is an intermediary between file descriptors and * inodes. A slot is free if filp_count == 0. */ EXTERN struct filp { mask_bits filp_mode; /* RW bits, telling how file is opened */ int filp_count; /* how many file descriptors share this slot? */ struct inode *filp_ino; /* pointer to the inode */ file_pos filp_pos; /* file position */ } filp[NR_FILPS]; #define NIL_FILP (struct filp *) 0 /* indicates absence of a filp slot */ /* Device table. This table is indexed by major device number. It provides * the link between major device numbers and the routines that process them. */ EXTERN struct dmap { int (*dmap_open)(); int (*dmap_rw)(); int (*dmap_close)(); int dmap_task; } dmap[]; /* Tables sizes */ #define NR_ZONE_NUMS 9 /* # zone numbers in an inode */ #define NR_BUFS 20 /* # blocks in the buffer cache */ #define NR_BUF_HASH 32 /* size of buf hash table; MUST BE POWER OF 2*/ #define NR_FDS 20 /* max file descriptors per process */ #define NR_FILPS 64 /* # slots in filp table */ #define I_MAP_SLOTS 4 /* max # of blocks in the inode bit map */ #define ZMAP_SLOTS 6 /* max # of blocks in the zone bit map */ #define NR_INODES 32 /* # slots in "in core" inode table */ #define NR_SUPERS 5 /* # slots in super block table */ #define NAME_SIZE 14 /* # bytes in a directory component */ #define FS_STACK_BYTES 512 /* size of file system stack */ /* Miscellaneous constants */ #define SUPER_MAGIC 0x137F /* magic number contained in super-block */ #define SU_UID (uid) 0 /* super_user's uid */ #define SYS_UID (uid) 0 /* uid for processes MM and INIT */ #define SYS_GID (gid) 0 /* gid for processes MM and INIT */ #define NORMAL 0 /* forces get_block to do disk read */ #define NO_READ 1 /* prevents get_block from doing disk read */ #define XPIPE 0 /* used in fp_task when suspended on pipe */ #define NO_BIT (bit_nr) 0 /* returned by alloc_bit() to signal failure */ #define DUP_MASK 0100 /* mask to distinguish dup2 from dup */ #define LOOK_UP 0 /* tells search_dir to lookup string */ #define ENTER 1 /* tells search_dir to make dir entry */ #define DELETE 2 /* tells search_dir to delete entry */ #define CLEAN 0 /* disk and memory copies identical */ #define DIRTY 1 /* disk and memory copies differ */ #define BOOT_BLOCK (block_nr) 0 /* block number of boot block */ #define SUPER_BLOCK (block_nr)1 /* block number of super block */ #define ROOT_INODE (inode_nr) 1 /* inode number for root directory */ /* Derived sizes */ #define ZONE_NUM_SIZE sizeof(zone_nr) /* # bytes in zone nr*/ #define NR_DZONE_NUM (NR_ZONE_NUMS-2) /* # zones in inode */ #define DIR_ENTRY_SIZE sizeof(dir_struct) /* # bytes/dir entry */ #define INODES_PER_BLOCK (BLOCK_SIZE/INODE_SIZE) /* # inodes/disk blk */ #define INODE_SIZE (sizeof (d_inode)) /* bytes in disk inode*/ #define NR_DIR_ENTRIES (BLOCK_SIZE/DIR_ENTRY_SIZE) /* # dir entries/blk*/ #define NR_INDIRECTS (BLOCK_SIZE/ZONE_NUM_SIZE) /* # zones/indir blk */ #define INTS_PER_BLOCK (BLOCK_SIZE/sizeof(int)) /* # integers/blk */ #define SUPER_SIZE sizeof(struct super_block) /* super_block size */ #define PIPE_SIZE (NR_DZONE_NUM*BLOCK_SIZE) /* pipe size in bytes*/ #define MAX_ZONES (NR_DZONE_NUM+NR_INDIRECTS+(long)NR_INDIRECTS*NR_INDIRECTS) /* max # of zones in a file */ #define printf printk /* The file system maintains a buffer cache to reduce the number of disk * accesses needed. Whenever a read or write to the disk is done, a check is * first made to see if the block is in the cache. This file manages the * cache. * * The entry points into this file are: * get_block: request to fetch a block for reading or writing from cache * put_block: return a block previously requested with get_block * alloc_zone: allocate a new zone (to increase the length of a file) * free_zone: release a zone (when a file is removed) * rw_block: read or write a block from the disk itself * invalidate: remove all the cache blocks on some device */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "super.h" /*===========================================================================* * get_block * *===========================================================================*/ PUBLIC struct buf *get_block(dev, block, only_search) register dev_nr dev; /* on which device is the block? */ register block_nr block; /* which block is wanted? */ int only_search; /* if NO_READ, don't read, else act normal */ { /* Check to see if the requested block is in the block cache. If so, return * a pointer to it. If not, evict some other block and fetch it (unless * 'only_search' is 1). All blocks in the cache, whether in use or not, * are linked together in a chain, with 'front' pointing to the least recently * used block and 'rear' to the most recently used block. If 'only_search' is * 1, the block being requested will be overwritten in its entirety, so it is * only necessary to see if it is in the cache; if it is not, any free buffer * will do. It is not necessary to actually read the block in from disk. * In addition to the LRU chain, there is also a hash chain to link together * blocks whose block numbers end with the same bit strings, for fast lookup. */ register struct buf *bp, *prev_ptr; /* Search the list of blocks not currently in use for (dev, block). */ bp = buf_hash[block & (NR_BUF_HASH - 1)]; /* search the hash chain */ if (dev != NO_DEV) { while (bp != NIL_BUF) { if (bp->b_blocknr == block && bp->b_dev == dev) { /* Block needed has been found. */ if (bp->b_count == 0) bufs_in_use++; bp->b_count++; /* record that block is in use */ return(bp); } else { /* This block is not the one sought. */ bp = bp->b_hash; /* move to next block on hash chain */ } } } /* Desired block is not on available chain. Take oldest block ('front'). * However, a block that is aready in use (b_count > 0) may not be taken. */ if (bufs_in_use == NR_BUFS) panic("All buffers in use", NR_BUFS); bufs_in_use++; /* one more buffer in use now */ bp = front; while (bp->b_count > 0 && bp->b_next != NIL_BUF) bp = bp->b_next; if (bp == NIL_BUF || bp->b_count > 0) panic("No free buffer", NO_NUM); /* Remove the block that was just taken from its hash chain. */ prev_ptr = buf_hash[bp->b_blocknr & (NR_BUF_HASH - 1)]; if (prev_ptr == bp) { buf_hash[bp->b_blocknr & (NR_BUF_HASH - 1)] = bp->b_hash; } else { /* The block just taken is not on the front of its hash chain. */ while (prev_ptr->b_hash != NIL_BUF) if (prev_ptr->b_hash == bp) { prev_ptr->b_hash = bp->b_hash; /* found it */ break; } else { prev_ptr = prev_ptr->b_hash; /* keep looking */ } } /* If the block taken is dirty, make it clean by rewriting it to disk. */ if (bp->b_dirt == DIRTY && bp->b_dev != NO_DEV) rw_block(bp, WRITING); /* Fill in block's parameters and add it to the hash chain where it goes. */ bp->b_dev = dev; /* fill in device number */ bp->b_blocknr = block; /* fill in block number */ bp->b_count++; /* record that block is being used */ bp->b_hash = buf_hash[bp->b_blocknr & (NR_BUF_HASH - 1)]; buf_hash[bp->b_blocknr & (NR_BUF_HASH - 1)] = bp; /* add to hash list */ /* Go get the requested block, unless only_search = NO_READ. */ if (dev != NO_DEV && only_search == NORMAL) rw_block(bp, READING); return(bp); /* return the newly acquired block */ } /*===========================================================================* * put_block * *===========================================================================*/ PUBLIC put_block(bp, block_type) register struct buf *bp; /* pointer to the buffer to be released */ int block_type; /* INODE_BLOCK, DIRECTORY_BLOCK, or whatever */ { /* Return a block to the list of available blocks. Depending on 'block_type' * it may be put on the front or rear of the LRU chain. Blocks that are * expected to be needed again shortly (e.g., partially full data blocks) * go on the rear; blocks that are unlikely to be needed again shortly * (e.g., full data blocks) go on the front. Blocks whose loss can hurt * the integrity of the file system (e.g., inode blocks) are written to * disk immediately if they are dirty. */ register struct buf *next_ptr, *prev_ptr; if (bp == NIL_BUF) return; /* it is easier to check here than in caller */ /* If block is no longer in use, first remove it from LRU chain. */ bp->b_count--; /* there is one use fewer now */ if (bp->b_count > 0) return; /* block is still in use */ bufs_in_use--; /* one fewer block buffers in use */ next_ptr = bp->b_next; /* successor on LRU chain */ prev_ptr = bp->b_prev; /* predecessor on LRU chain */ if (prev_ptr != NIL_BUF) prev_ptr->b_next = next_ptr; else front = next_ptr; /* this block was at front of chain */ if (next_ptr != NIL_BUF) next_ptr->b_prev = prev_ptr; else rear = prev_ptr; /* this block was at rear of chain */ /* Put this block back on the LRU chain. If the ONE_SHOT bit is set in * 'block_type', the block is not likely to be needed again shortly, so put * it on the front of the LRU chain where it will be the first one to be * taken when a free buffer is needed later. */ if (block_type & ONE_SHOT) { /* Block probably won't be needed quickly. Put it on front of chain. * It will be the next block to be evicted from the cache. */ bp->b_prev = NIL_BUF; bp->b_next = front; if (front == NIL_BUF) rear = bp; /* LRU chain was empty */ else front->b_prev = bp; front = bp; } else { /* Block probably will be needed quickly. Put it on rear of chain. * It will not be evicted from the cache for a long time. */ bp->b_prev = rear; bp->b_next = NIL_BUF; if (rear == NIL_BUF) front = bp; else rear->b_next = bp; rear = bp; } /* Some blocks are so important (e.g., inodes, indirect blocks) that they * should be written to the disk immediately to avoid messing up the file * system in the event of a crash. */ if ((block_type & WRITE_IMMED) && bp->b_dirt==DIRTY && bp->b_dev != NO_DEV) rw_block(bp, WRITING); /* Super blocks must not be cached, lest mount use cached block. */ if (block_type == ZUPER_BLOCK) bp->b_dev = NO_DEV; } /*===========================================================================* * alloc_zone * *===========================================================================*/ PUBLIC zone_nr alloc_zone(dev, z) dev_nr dev; /* device where zone wanted */ zone_nr z; /* try to allocate new zone near this one */ { /* Allocate a new zone on the indicated device and return its number. */ bit_nr b, bit; struct super_block *sp; int major, minor; extern bit_nr alloc_bit(); extern struct super_block *get_super(); /* Note that the routine alloc_bit() returns 1 for the lowest possible * zone, which corresponds to sp->s_firstdatazone. To convert a value * between the bit number, 'b', used by alloc_bit() and the zone number, 'z', * stored in the inode, use the formula: * z = b + sp->s_firstdatazone - 1 * Alloc_bit() never returns 0, since this is used for NO_BIT (failure). */ sp = get_super(dev); /* find the super_block for this device */ bit = (bit_nr) z - (sp->s_firstdatazone - 1); b = alloc_bit(sp->s_zmap, (bi1345t_nr) sp->s_nzones - sp->s_firstdatazone + 1, sp->s_zmap_blocks, bit); if (b == NO_BIT) { err_code = ENOSPC; major = (int) (sp->s_dev >> MAJOR) & BYTE; minor = (int) (sp->s_dev >> MINOR) & BYTE; if (sp->s_dev == ROOT_DEV) printf("No space on root device (RAM disk)\n"); else printf("No space on device %d/%d\n", major, minor); return(NO_ZONE); } return(sp->s_firstdatazone - 1 + (zone_nr) b); } /*===========================================================================* * free_zone * *===========================================================================*/ PUBLIC free_zone(dev, numb) dev_nr dev; /* device where zone located */ zone_nr numb; /* zone to be returned */ { /* Return a zone. */ register struct super_block *sp; extern struct super_block *get_super(); if (numb == NO_ZONE) return; /* checking here easier than in caller */ /* Locate the appropriate super_block and return bit. */ sp = get_super(dev); free_bit(sp->s_zmap, (bit_nr) numb - (sp->s_firstdatazone - 1) ); } /*===========================================================================* * rw_block * *===========================================================================*/ PUBLIC rw_block(bp, rw_flag) register struct buf *bp; /* buffer pointer */ int rw_flag; /* READING or WRITING */ { /* Read or write a disk block. This is the only routine in which actual disk * I/O is invoked. If an error occurs, a message is printed here, but the error * is not reported to the caller. If the error occurred while purging a block * from the cache, it is not clear what the caller could do about it anyway. */ int r; long pos; dev_nr dev; extern int rdwt_err; if (bp->b_dev != NO_DEV) { pos = (long) bp->b_blocknr * BLOCK_SIZE; r = dev_io(rw_flag, bp->b_dev, pos, BLOCK_SIZE, FS_PROC_NR, bp->b_data); if (r < 0) { dev = bp->b_dev; if (r != EOF) { printf("Unrecoverable disk error on device %d/%d, block %d\n", (dev>>MAJOR)&BYTE, (dev>>MINOR)&BYTE, bp->b_blocknr); } else { bp->b_dev = NO_DEV; /* invalidate block */ } rdwt_err = r; /* report error to interested parties */ } } bp->b_dirt = CLEAN; } /*===========================================================================* * invalidate * *===========================================================================*/ PUBLIC invalidate(device) dev_nr device; /* device whose blocks are to be purged */ { /* Remove all the blocks belonging to some device from the cache. */ register struct buf *bp; for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) if (bp->b_dev == device) bp->b_dev = NO_DEV; } /* Buffer (block) cache. To acquire a block, a routine calls get_block(), * telling which block it wants. The block is then regarded as "in use" * and has its 'b_count' field incremented. All the blocks, whether in use * or not, are chained together in an LRU list, with 'front' pointing * to the least recently used block, and 'rear' to the most recently used * block. A reverse chain, using the field b_prev is also maintained. * Usage for LRU is measured by the time the put_block() is done. The second * parameter to put_block() can violate the LRU order and put a block on the * front of the list, if it will probably not be needed soon. If a block * is modified, the modifying routine must set b_dirt to DIRTY, so the block * will eventually be rewritten to the disk. */ EXTERN struct buf { /* Data portion of the buffer. */ union { char b__data[BLOCK_SIZE]; /* ordinary user data */ dir_struct b__dir[NR_DIR_ENTRIES]; /* directory block */ zone_nr b__ind[NR_INDIRECTS]; /* indirect block */ d_inode b__inode[INODES_PER_BLOCK]; /* inode block */ int b__int[INTS_PER_BLOCK]; /* block full of integers */ } b; /* Header portion of the buffer. */ struct buf *b_next; /* used to link bufs in a chain */ struct buf *b_prev; /* used to link bufs the other way */ struct buf *b_hash; /* used to link bufs on hash chains */ block_nr b_blocknr; /* block number of its (minor) device */ dev_nr b_dev; /* major | minor device where block resides */ char b_dirt; /* CLEAN or DIRTY */ char b_count; /* number of users of this buffer */ } buf[NR_BUFS]; /* A block is free if b_dev == NO_DEV. */ #define NIL_BUF (struct buf *) 0 /* indicates absence of a buffer */ /* These defs make it possible to use to bp->b_data instead of bp->b.b__data */ #define b_data b.b__data #define b_dir b.b__dir #define b_ind b.b__ind #define b_inode b.b__inode #define b_int b.b__int EXTERN struct buf *buf_hash[NR_BUF_HASH]; /* the buffer hash table */ EXTERN struct buf *front; /* points to least recently used free block */ EXTERN struct buf *rear; /* points to most recently used free block */ EXTERN int bufs_in_use; /* # bufs currently in use (not on free list) */ /* When a block is released, the type of usage is passed to put_block(). */ #define WRITE_IMMED 0100 /* block should be written to disk now */ #define ONE_SHOT 0200 /* set if block not likely to be needed soon */ #define INODE_BLOCK 0 + WRITE_IMMED /* inode block */ #define DIRECTORY_BLOCK 1 + WRITE_IMMED /* directory block */ #define INDIRECT_BLOCK 2 + WRITE_IMMED /* pointer block */ #define I_MAP_BLOCK 3 + WRITE_IMMED + ONE_SHOT /* inode bit map */ #define ZMAP_BLOCK 4 + WRITE_IMMED + ONE_SHOT /* free zone map */ #define ZUPER_BLOCK 5 + WRITE_IMMED + ONE_SHOT /* super block */ #define FULL_DATA_BLOCK 6 /* data, fully used */ #define PARTIAL_DATA_BLOCK 7 /* data, partly used */ /* When a needed block is not in the cache, it must be fetched from the disk. * Special character files also require I/O. The routines for these are here. * * The entry points in this file are: * dev_open: called when a special file is opened * dev_close: called when a special file is closed * dev_io: perform a read or write on a block or character device * do_ioctl: perform the IOCTL system call * rw_dev: procedure that actually calls the kernel tasks * rw_dev2: procedure that actually calls task for /dev/tty * no_call: dummy procedure (e.g., used when device need not be opened) */ #include "../h/const.h" #include "../h/type.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "dev.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" PRIVATE message dev_mess; PRIVATE major, minor, task; extern max_major; /*===========================================================================* * dev_open * *===========================================================================*/ PUBLIC int dev_open(dev, mod) dev_nr dev; /* which device to open */ int mod; /* how to open it */ { /* Special files may need special processing upon open. */ find_dev(dev); (*dmap[major].dmap_open)(task, &dev_mess); return(dev_mess.REP_STATUS); } /*===========================================================================* * dev_close * *===========================================================================*/ PUBLIC dev_close(dev) dev_nr dev; /* which device to close */ { /* This procedure can be used when a special file needs to be closed. */ find_dev(dev); (*dmap[major].dmap_close)(task, &dev_mess); } /*===========================================================================* * dev_io * *===========================================================================*/ PUBLIC int dev_io(rw_flag, dev, pos, bytes, proc, buff) int rw_flag; /* READING or WRITING */ dev_nr dev; /* major-minor device number */ long pos; /* byte position */ int bytes; /* how many bytes to transfer */ int proc; /* in whose address space is buff? */ char *buff; /* virtual address of the buffer */ { /* Read or write from a device. The parameter 'dev' tells which one. */ find_dev(dev); /* Set up the message passed to task. */ dev_mess.m_type = (rw_flag == READING ? DISK_READ : DISK_WRITE); dev_mess.DEVICE = (dev >> MINOR) & BYTE; dev_mess.POSITION = pos; dev_mess.PROC_NR = proc; dev_mess.ADDRESS = buff; dev_mess.COUNT = bytes; /* Call the task. */ (*dmap[major].dmap_rw)(task, &dev_mess); /* Task has completed. See if call completed. */ if (dev_mess.REP_STATUS == SUSPEND) suspend(task); /* suspend user */ return(dev_mess.REP_STATUS); } /*===========================================================================* * do_ioctl * *===========================================================================*/ PUBLIC do_ioctl() { /* Perform the ioctl(ls_fd, request, argx) system call (uses m2 fmt). */ struct filp *f; register struct inode *rip; extern struct filp *get_filp(); if ( (f = get_filp(ls_fd)) == NIL_FILP) return(err_code); rip = f->filp_ino; /* get inode pointer */ if ( (rip->i_mode & I_TYPE) != I_CHAR_SPECIAL) return(ENOTTY); find_dev(rip->i_zone[0]); dev_mess.m_type = TTY_IOCTL; dev_mess.PROC_NR = who; dev_mess.TTY_LINE = minor; dev_mess.TTY_REQUEST = m.TTY_REQUEST; dev_mess.TTY_SPEK = m.TTY_SPEK; dev_mess.TTY_FLAGS = m.TTY_FLAGS; /* Call the task. */ (*dmap[major].dmap_rw)(task, &dev_mess); /* Task has completed. See if call completed. */ if (dev_mess.m_type == SUSPEND) suspend(task); /* User must be suspended. */ m1.TTY_SPEK = dev_mess.TTY_SPEK; /* erase and kill */ m1.TTY_FLAGS = dev_mess.TTY_FLAGS; /* flags */ return(dev_mess.REP_STATUS); } /*===========================================================================* * find_dev * *===========================================================================*/ PRIVATE find_dev(dev) dev_nr dev; /* device */ { /* Extract the major and minor device number from the parameter. */ major = (dev >> MAJOR) & BYTE; /* major device number */ minor = (dev >> MINOR) & BYTE; /* minor device number */ if (major == 0 || major >= max_major) panic("bad major dev", major); task = dmap[major].dmap_task; /* which task services the device */ dev_mess.DEVICE = minor; } /*===========================================================================* * rw_dev * *===========================================================================*/ PUBLIC rw_dev(task_nr, mess_ptr) int task_nr; /* which task to call */ message *mess_ptr; /* pointer to message for task */ { /* All file system I/O ultimately comes down to I/O on major/minor device * pairs. These lead to calls on the following routines via the dmap table. */ int proc_nr; proc_nr = mess_ptr->PROC_NR; if (sendrec(task_nr, mess_ptr) != OK) panic("rw_dev: can't send", NO_NUM); while (mess_ptr->REP_PROC_NR != proc_nr) { /* Instead of the reply to this request, we got a message for an * earlier request. Handle it and go receive again. */ revive(mess_ptr->REP_PROC_NR, mess_ptr->REP_STATUS); receive(task_nr, mess_ptr); } } /*===========================================================================* * rw_dev2 * *===========================================================================*/ PUBLIC rw_dev2(dummy, mess_ptr) int dummy; /* not used - for compatibility with rw_dev() */ message *mess_ptr; /* pointer to message for task */ { /* This routine is only called for one device, namely /dev/tty. It's job * is to change the message to use the controlling terminal, instead of the * major/minor pair for /dev/tty itself. */ int task_nr, major_device; major_device = (fp->fs_tty >> MAJOR) & BYTE; task_nr = dmap[major_device].dmap_task; /* task for controlling tty */ mess_ptr->DEVICE = (fp->fs_tty >> MINOR) & BYTE; rw_dev(task_nr, mess_ptr); } /*===========================================================================* * no_call * *===========================================================================*/ PUBLIC int no_call(task_nr, m_ptr) int task_nr; /* which task */ message *m_ptr; /* message pointer */ { /* Null operation always succeeds. */ m_ptr->REP_STATUS = OK; } /* This file contains the procedures that manipulate file descriptors. * * The entry points into this file are * get_fd: look for free file descriptor and free filp slots * get_filp: look up the filp entry for a given file descriptor * find_filp: find a filp slot that points to a given inode */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" /*===========================================================================* * get_fd * *===========================================================================*/ PUBLIC int get_fd(bits, k, fpt) mask_bits bits; /* mode of the file to be created (RWX bits) */ int *k; /* place to return file descriptor */ struct filp **fpt; /* place to return filp slot */ { /* Look for a free file descriptor and a free filp slot. Fill in the mode word * in the latter, but don't claim either one yet, since the open() or creat() * may yet fail. */ register struct filp *f; register int i; *k = -1; /* we need a way to tell if file desc found */ /* Search the fproc table for a free file descriptor. */ for (i = 0; i < NR_FDS; i++) { if (fp->fp_filp[i] == NIL_FILP) { /* A file descriptor has been located. */ *k = i; break; } } /* Check to see if a file descriptor has been found. */ if (*k < 0) return(EMFILE); /* this is why we initialized k to -1 */ /* Now that a file descriptor has been found, look for a free filp slot. */ for (f = &filp[0]; f < &filp[NR_FILPS]; f++) { if (f->filp_count == 0) { f->filp_mode = bits; f->filp_pos = 0L; *fpt = f; return(OK); } } /* If control passes here, the filp table must be full. Report that back. */ return(ENFILE); } /*===========================================================================* * get_filp * *===========================================================================*/ PUBLIC struct filp *get_filp(fild) int fild; /* file descriptor */ { /* See if 'fild' refers to a valid file descr. If so, return its filp ptr. */ err_code = EBADF; if (fild < 0 || fild >= NR_FDS ) return(NIL_FILP); return(fp->fp_filp[fild]); /* may also be NIL_FILP */ } /*===========================================================================* * find_filp * *===========================================================================*/ PUBLIC struct filp *find_filp(rip, bits) register struct inode *rip; /* inode referred to by the filp to be found */ int bits; /* mode of the filp to be found (RWX bits) */ { /* Find a filp slot that refers to the inode 'rip' in a way as described * by the mode bit 'bits'. Used for determining whether somebody is still * interested in either end of a pipe; other applications are conceivable. * Like 'get_fd' it performs its job by linear search through the filp table. */ register struct filp *f; for (f = &filp[0]; f < &filp[NR_FILPS]; f++) { if (f->filp_count != 0 && f->filp_ino == rip && (f->filp_mode & bits)){ return(f); } } /* If control passes here, the filp wasn't there. Report that back. */ return(NIL_FILP); } /* This file manages the inode table. There are procedures to allocate and * deallocate inodes, acquire, erase, and release them, and read and write * them from the disk. * * The entry points into this file are * get_inode: search inode table for a given inode; if not there, read it * put_inode: indicate that an inode is no longer needed in memory * alloc_inode: allocate a new, unused inode * wipe_inode: erase some fields of a newly allocated inode * free_inode: mark an inode as available for a new file * rw_inode: read a disk block and extract an inode, or corresp. write * dup_inode: indicate that someone else is using an inode table entry */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "super.h" /*===========================================================================* * get_inode * *===========================================================================*/ PUBLIC struct inode *get_inode(dev, numb) dev_nr dev; /* device on which inode resides */ inode_nr numb; /* inode number */ { /* Find a slot in the inode table, load the specified inode into it, and * return a pointer to the slot. If 'dev' == NO_DEV, just return a free slot. */ register struct inode *rip, *xp; /* Search the inode table both for (dev, numb) and a free slot. */ xp = NIL_INODE; for (rip = &inode[0]; rip < &inode[NR_INODES]; rip++) { if (rip->i_count > 0) { /* only check used slots for (dev, numb) */ if (rip->i_dev == dev && rip->i_num == numb) { /* This is the inode that we are looking for. */ rip->i_count++; return(rip); /* (dev, numb) found */ } } else xp = rip; /* remember this free slot for later */ } /* Inode we want is not currently in use. Did we find a free slot? */ if (xp == NIL_INODE) { /* inode table completely full */ err_code = ENFILE; return(NIL_INODE); } /* A free inode slot has been located. Load the inode into it. */ xp->i_dev = dev; xp->i_num = numb; xp->i_count = 1; if (dev != NO_DEV) rw_inode(xp, READING); /* get inode from disk */ return(xp); } /*===========================================================================* * put_inode * *===========================================================================*/ PUBLIC put_inode(rip) register struct inode *rip; /* pointer to inode to be released */ { /* The caller is no longer using this inode. If no one else is using it either * write it back to the disk immediately. If it has no links, truncate it and * return it to the pool of available inodes. */ if (rip == NIL_INODE) return; /* checking here is easier than in caller */ if (--rip->i_count == 0) { /* i_count == 0 means no one is using it now */ if ((rip->i_nlinks & BYTE) == 0) { /* i_nlinks == 0 means free the inode. */ truncate(rip); /* return all the disk blocks */ rip->i_mode = I_NOT_ALLOC; /* clear I_TYPE field */ rip->i_pipe = NO_PIPE; free_inode(rip->i_dev, rip->i_num); } if (rip->i_dirt == DIRTY) rw_inode(rip, WRITING); } } /*===========================================================================* * alloc_inode * *===========================================================================*/ PUBLIC struct inode *alloc_inode(dev, bits) dev_nr dev; /* device on which to allocate the inode */ mask_bits bits; /* mode of the inode */ { /* Allocate a free inode on 'dev', and return a pointer to it. */ register struct inode *rip; register struct super_block *sp; int major, minor; inode_nr numb; bit_nr b; extern bit_nr alloc_bit(); extern struct inode *get_inode(); extern struct super_block *get_super(); /* Acquire an inode from the bit map. */ sp = get_super(dev); /* get pointer to super_block */ b=alloc_bit(sp->s_imap, (bit_nr)sp->s_ninodes+1, sp->s_imap_blocks,(bit_nr)0); if (b == NO_BIT) { err_code = ENFILE; major = (int) (sp->s_dev >> MAJOR) & BYTE; minor = (int) (sp->s_dev >> MINOR) & BYTE; if (sp->s_dev == ROOT_DEV) printf("Out of i-nodes on root device (RAM disk)\n"); else printf("Out of i-nodes on device %d/%d\n", major, minor); return(NIL_INODE); } numb = (inode_nr) b; /* Try to acquire a slot in the inode table. */ if ( (rip = get_inode(NO_DEV, numb)) == NIL_INODE) { /* No inode table slots available. Free the inode just allocated. */ free_bit(sp->s_imap, b); } else { /* An inode slot is available. Put the inode just allocated into it. */ rip->i_mode = bits; rip->i_nlinks = (links) 0; rip->i_uid = fp->fp_effuid; rip->i_gid = fp->fp_effgid; rip->i_dev = dev; /* was provisionally set to NO_DEV */ /* The fields not cleared already are cleared in wipe_inode(). They have * been put there because truncate() needs to clear the same fields if * the file happens to be open while being truncated. It saves space * not to repeat the code twice. */ wipe_inode(rip); } return(rip); } /*===========================================================================* * wipe_inode * *===========================================================================*/ PUBLIC wipe_inode(rip) register struct inode *rip; /* The inode to be erased. */ { /* Erase some fields in the inode. This function is called from alloc_inode() * when a new inode is to be allocated, and from truncate(), when an existing * inode is to be truncated. */ register int i; extern real_time clock_time(); rip->i_size = 0; rip->i_modtime = clock_time(); rip->i_dirt = DIRTY; for (i = 0; i < NR_ZONE_NUMS; i++) rip->i_zone[i] = NO_ZONE; } /*===========================================================================* * free_inode * *===========================================================================*/ PUBLIC free_inode(dev, numb) dev_nr dev; /* on which device is the inode */ inode_nr numb; /* number of inode to be freed */ { /* Return an inode to the pool of unallocated inodes. */ register struct super_block *sp; extern struct super_block *get_super(); /* Locate the appropriate super_block. */ sp = get_super(dev); free_bit(sp->s_imap, (bit_nr) numb); } /*===========================================================================* * rw_inode * *===========================================================================*/ PUBLIC rw_inode(rip, rw_flag) register struct inode *rip; /* pointer to inode to be read/written */ int rw_flag; /* READING or WRITING */ { /* An entry in the inode table is to be copied to or from the disk. */ register struct buf *bp; register d_inode *dip; register struct super_block *sp; block_nr b; extern struct buf *get_block(); extern struct super_block *get_super(); /* Get the block where the inode resides. */ sp = get_super(rip->i_dev); b = (block_nr) (rip->i_num - 1)/INODES_PER_BLOCK + sp->s_imap_blocks + sp->s_zmap_blocks + 2; bp = get_block(rip->i_dev, b, NORMAL); dip = bp->b_inode + (rip->i_num - 1) % INODES_PER_BLOCK; /* Do the read or write. */ if (rw_flag == READING) { copy((char *)rip, (char *) dip, INODE_SIZE); /* copy from blk to inode */ } else { copy((char *)dip, (char *) rip, INODE_SIZE); /* copy from inode to blk */ bp->b_dirt = DIRTY; } put_block(bp, INODE_BLOCK); rip->i_dirt = CLEAN; } /*===========================================================================* * dup_inode * *===========================================================================*/ PUBLIC dup_inode(ip) struct inode *ip; /* The inode to be duplicated. */ { /* This routine is a simplified form of get_inode() for the case where * the inode pointer is already known. */ ip->i_count++; } K/* This file handles the LINK and UNLINK system calls. It also deals with * deallocating the storage used by a file when the last UNLINK is done to a * file and the blocks must be returned to the free block pool. * * The entry points into this file are * do_link: perform the LINK system call * do_unlink: perform the UNLINK system call * truncate: release all the blocks associated with an inode */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" /*===========================================================================* * do_link * *===========================================================================*/ PUBLIC int do_link() { /* Perform the link(name, name2) system call. */ register struct inode *ip, *rip; register int r; char string[NAME_SIZE]; struct inode *new_ip; extern struct inode *advance(), *last_dir(), *eat_path(); /* See if 'name' (file to be linked) exists. */ if (fetch_name(name1, name1_length, M1) != OK) return(err_code); if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); /* Check to see if the file has maximum number of links already. */ r = OK; if ( (rip->i_nlinks & BYTE) == MAX_LINKS) r = EMLINK; /* Only super_user may link to directories. */ if (r == OK) if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM; /* If error with 'name', return the inode. */ if (r != OK) { put_inode(rip); return(r); } /* Does the final directory of 'name2' exist? */ if (fetch_name(name2, name2_length, M1) != OK) { put_inode(rip); return(err_code); } if ( (ip = last_dir(user_path, string)) == NIL_INODE) r = err_code; /* If 'name2' exists in full (even if no space) set 'r' to error. */ if (r == OK) { if ( (new_ip = advance(ip, string)) == NIL_INODE) { r = err_code; if (r == ENOENT) r = OK; } else { put_inode(new_ip); r = EEXIST; } } /* Check for links across devices. */ if (r == OK) if (rip->i_dev != ip->i_dev) r = EXDEV; /* Try to link. */ if (r == OK) r = search_dir(ip, string, &rip->i_num, ENTER); /* If success, register the linking. */ if (r == OK) { rip->i_nlinks++; rip->i_dirt = DIRTY; } /* Done. Release both inodes. */ put_inode(rip); put_inode(ip); return(r); } /*===========================================================================* * do_unlink * *===========================================================================*/ PUBLIC int do_unlink() { /* Perform the unlink(name) system call. */ register struct inode *rip, *rlast_dir_ptr; register int r; inode_nr numb; char string[NAME_SIZE]; extern struct inode *advance(), *last_dir(); /* Get the last directory in the path. */ if (fetch_name(name, name_length, M3) != OK) return(err_code); if ( (rlast_dir_ptr = last_dir(user_path, string)) == NIL_INODE) return(err_code); /* The last directory exists. Does the file also exist? */ r = OK; if ( (rip = advance(rlast_dir_ptr, string)) == NIL_INODE) r = err_code; /* If error, return inode. */ if (r != OK) { put_inode(rlast_dir_ptr); return(r); } /* See if the file is a directory. */ if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM; /* only super_user can unlink directory */ if (r == OK) r = search_dir(rlast_dir_ptr, string, &numb, DELETE); if (r == OK) { rip->i_nlinks--; rip->i_dirt = DIRTY; } /* If unlink was possible, it has been done, otherwise it has not. */ put_inode(rip); put_inode(rlast_dir_ptr); return(r); } /*===========================================================================* * truncate * *===========================================================================*/ PUBLIC truncate(rip) register struct inode *rip; /* pointer to inode to be truncated */ { /* Remove all the zones from the inode 'rip' and mark it dirty. */ register file_pos position; register zone_type zone_size; register block_nr b; register zone_nr z, *iz; register int scale; register struct buf *bp; register dev_nr dev; extern struct buf *get_block(); extern block_nr read_map(); dev = rip->i_dev; /* device on which inode resides */ scale = scale_factor(rip); zone_size = (zone_type) BLOCK_SIZE << scale; if (rip->i_pipe == I_PIPE) rip->i_size = PIPE_SIZE; /* pipes can shrink */ /* Step through the file a zone at a time, finding and freeing the zones. */ for (position = 0; position < rip->i_size; position += zone_size) { if ( (b = read_map(rip, position)) != NO_BLOCK) { z = (zone_nr) b >> scale; free_zone(dev, z); } } /* All the data zones have been freed. Now free the indirect zones. */ free_zone(dev, rip->i_zone[NR_DZONE_NUM]); /* single indirect zone */ if ( (z = rip->i_zone[NR_DZONE_NUM+1]) != NO_ZONE) { b = (block_nr) z << scale; bp = get_block(dev, b, NORMAL); /* get double indirect zone */ for (iz = &bp->b_ind[0]; iz < &bp->b_ind[NR_INDIRECTS]; iz++) { free_zone(dev, *iz); } /* Now free the double indirect zone itself. */ put_block(bp, INDIRECT_BLOCK); free_zone(dev, z); } /* The inode being truncated might currently be open, so certain fields must * be cleared immediately, even though these fields are also cleared by * alloc_inode(). The function wipe_inode() does the dirty work in both cases. */ wipe_inode(rip); } /* This file contains the main program of the File System. It consists of * a loop that gets messages requesting work, carries out the work, and sends * replies. * * The entry points into this file are * main: main program of the File System * reply: send a reply to a process after the requested work is done */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" #include "super.h" #define M64K 0xFFFF0000L /* 16 bit mask for DMA check */ #define INFO 2 /* where in data_org is info from build */ #define MAX_RAM 512 /* maxium RAM disk size in blocks */ /*===========================================================================* * main * *===========================================================================*/ PUBLIC main() { /* This is the main program of the file system. The main loop consists of * three major activities: getting new work, processing the work, and sending * the reply. This loop never terminates as long as the file system runs. */ int error; extern int (*call_vector[NCALLS])(); fs_init(); /* This is the main loop that gets work, processes it, and sends replies. */ while (TRUE) { get_work(); /* sets who and fs_call */ fp = &fproc[who]; /* pointer to proc table struct */ super_user = (fp->fp_effuid == SU_UID ? TRUE : FALSE); /* su? */ dont_reply = FALSE; /* in other words, do reply is default */ /* Call the internal function that does the work. */ if (fs_call < 0 || fs_call >= NCALLS) error = E_BAD_CALL; else error = (*call_vector[fs_call])(); /* Copy the results back to the user and send reply. */ if (dont_reply) continue; reply(who, error); if (rdahed_inode != NIL_INODE) read_ahead(); /* do block read ahead */ } } /*===========================================================================* * get_work * *===========================================================================*/ PRIVATE get_work() { /* Normally wait for new input. However, if 'reviving' is * nonzero, a suspended process must be awakened. */ register struct fproc *rp; if (reviving != 0) { /* Revive a suspended process. */ for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) if (rp->fp_revived == REVIVING) { who = rp - fproc; fs_call = rp->fp_fd & BYTE; fd = (rp->fp_fd >>8) & BYTE; buffer = rp->fp_buffer; nbytes = rp->fp_nbytes; rp->fp_suspended = NOT_SUSPENDED; /* no longer hanging*/ rp->fp_revived = NOT_REVIVING; reviving--; return; } panic("get_work couldn't revive anyone", NO_NUM); } /* Normal case. No one to revive. */ if (receive(ANY, &m) != OK) panic("fs receive error", NO_NUM); who = m.m_source; fs_call = m.m_type; } /*===========================================================================* * reply * *===========================================================================*/ PUBLIC reply(whom, result) int whom; /* process to reply to */ int result; /* result of the call (usually OK or error #) */ { /* Send a reply to a user process. It may fail (if the process has just * been killed by a signal, so don't check the return code. If the send * fails, just ignore it. */ reply_type = result; send(whom, &m1); } /*===========================================================================* * fs_init * *===========================================================================*/ PRIVATE fs_init() { /* Initialize global variables, tables, etc. */ register struct inode *rip; int i; extern struct inode *get_inode(); buf_pool(); /* initialize buffer pool */ load_ram(); /* Load RAM disk from root diskette. */ load_super(); /* Load super block for root device */ /* Initialize the 'fproc' fields for process 0 and process 2. */ for (i = 0; i < 3; i+= 2) { fp = &fproc[i]; rip = get_inode(ROOT_DEV, ROOT_INODE); fp->fp_rootdir = rip; dup_inode(rip); fp->fp_workdir = rip; fp->fp_realuid = (uid) SYS_UID; fp->fp_effuid = (uid) SYS_UID; fp->fp_realgid = (gid) SYS_GID; fp->fp_effgid = (gid) SYS_GID; fp->fp_umask = ~0; } /* Certain relations must hold for the file system to work at all. */ if (ZONE_NUM_SIZE != 2) panic("ZONE_NUM_SIZE != 2", NO_NUM); if (SUPER_SIZE > BLOCK_SIZE) panic("SUPER_SIZE > BLOCK_SIZE", NO_NUM); if(BLOCK_SIZE % INODE_SIZE != 0)panic("BLOCK_SIZE % INODE_SIZE != 0", NO_NUM); if (NR_FDS > 127) panic("NR_FDS > 127", NO_NUM); if (NR_BUFS < 6) panic("NR_BUFS < 6", NO_NUM); if (sizeof(d_inode) != 32) panic("inode size != 32", NO_NUM); } /*===========================================================================* * buf_pool * *===========================================================================*/ PRIVATE buf_pool() { /* Initialize the buffer pool. On the IBM PC, the hardware DMA chip is * not able to cross 64K boundaries, so any buffer that happens to lie * across such a boundary is not used. This is not very elegant, but all * the alternative solutions are as bad, if not worse. The fault lies with * the PC hardware. */ register struct buf *bp; vir_bytes low_off, high_off; phys_bytes org; extern phys_clicks get_base(); bufs_in_use = 0; front = &buf[0]; rear = &buf[NR_BUFS - 1]; for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) { bp->b_blocknr = NO_BLOCK; bp->b_dev = NO_DEV; bp->b_next = bp + 1; bp->b_prev = bp - 1; } buf[0].b_prev = NIL_BUF; buf[NR_BUFS - 1].b_next = NIL_BUF; /* Delete any buffers that span a 64K boundary. */ #ifdef i8088 for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) { org = get_base() << CLICK_SHIFT; /* phys addr where FS is */ low_off = (vir_bytes) bp->b_data; high_off = low_off + BLOCK_SIZE - 1; if (((org + low_off) & M64K) != ((org + high_off) & M64K)) { if (bp == &buf[0]) { front = &buf[1]; buf[1].b_prev = NIL_BUF; } else if (bp == &buf[NR_BUFS - 1]) { rear = &buf[NR_BUFS - 2]; buf[NR_BUFS - 2].b_next = NIL_BUF; } else { /* Delete a buffer in the middle. */ bp->b_prev->b_next = bp + 1; bp->b_next->b_prev = bp - 1; } } } #endif for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) bp->b_hash = bp->b_next; buf_hash[NO_BLOCK & (NR_BUF_HASH - 1)] = front; } /*===========================================================================* * load_ram * *===========================================================================*/ PRIVATE load_ram() { /* The root diskette contains a block-by-block image of the root file system * starting at 0. Go get it and copy it to the RAM disk. */ register struct buf *bp, *bp1; int count; long k_loaded; struct super_block *sp; block_nr i; phys_clicks ram_clicks, init_org, init_text_clicks, init_data_clicks; extern phys_clicks data_org[INFO + 2]; extern struct buf *get_block(); /* Get size of INIT by reading block on diskette where 'build' put it. */ init_org = data_org[INFO]; init_text_clicks = data_org[INFO + 1]; init_data_clicks = data_org[INFO + 2]; /* Get size of RAM disk by reading root file system's super block */ bp = get_block(BOOT_DEV, SUPER_BLOCK, NORMAL); /* get RAM super block */ copy(super_block, bp->b_data, sizeof(struct super_block)); sp = &super_block[0]; if (sp->s_magic != SUPER_MAGIC) panic("Diskette in drive 0 is not root file system", NO_NUM); count = sp->s_nzones << sp->s_log_zone_size; /* # blocks on root dev */ if (count > MAX_RAM) panic("RAM disk is too big. # blocks = ", count); ram_clicks = count * (BLOCK_SIZE/CLICK_SIZE); put_block(bp, FULL_DATA_BLOCK); /* Tell MM the origin and size of INIT, and the amount of memory used for the * system plus RAM disk combined, so it can remove all of it from the map. */ m1.m_type = BRK2; m1.m1_i1 = init_text_clicks; m1.m1_i2 = init_data_clicks; m1.m1_i3 = init_org + init_text_clicks + init_data_clicks + ram_clicks; m1.m1_p1 = (char *) init_org; if (sendrec(MM_PROC_NR, &m1) != OK) panicZ\]("FS Can't report to MM", NO_NUM); /* Tell RAM driver where RAM disk is and how big it is. */ m1.m_type = DISK_IOCTL; m1.DEVICE = RAM_DEV; m1.POSITION = (long) init_org + (long) init_text_clicks + init_data_clicks; m1.POSITION = m1.POSITION << CLICK_SHIFT; m1.COUNT = count; if (sendrec(MEM, &m1) != OK) panic("Can't report size to MEM", NO_NUM); /* Copy the blocks one at a time from the root diskette to the RAM */ printf("Loading RAM disk from root diskette. Loaded: 0K "); for (i = 0; i < count; i++) { bp = get_block(BOOT_DEV, (block_nr) i, NORMAL); bp1 = get_block(ROOT_DEV, i, NO_READ); copy(bp1->b_data, bp->b_data, BLOCK_SIZE); bp1->b_dirt = DIRTY; put_block(bp, I_MAP_BLOCK); put_block(bp1, I_MAP_BLOCK); k_loaded = ( (long) i * BLOCK_SIZE)/1024L; /* K loaded so far */ if (k_loaded % 5 == 0) printf("\b\b\b\b\b%3DK %c", k_loaded, 0); } printf("\rRAM disk loaded. Please remove root diskette. \n\n"); } /*===========================================================================* * load_super * *===========================================================================*/ PRIVATE load_super() { register struct super_block *sp; register struct inode *rip; extern struct inode *get_inode(); /* Initialize the super_block table. */ for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) sp->s_dev = NO_DEV; /* Read in super_block for the root file system. */ sp = &super_block[0]; sp->s_dev = ROOT_DEV; rw_super(sp,READING); rip = get_inode(ROOT_DEV, ROOT_INODE); /* inode for root dir */ /* Check super_block for consistency (is it the right diskette?). */ if ( (rip->i_mode & I_TYPE) != I_DIRECTORY || rip->i_nlinks < 3 || sp->s_magic != SUPER_MAGIC) panic("Root file system corrupted. Possibly wrong diskette.", NO_NUM); sp->s_imount = rip; dup_inode(rip); sp->s_isup = rip; sp->s_rd_only = 0; if (load_bit_maps(ROOT_DEV) != OK) panic("init: can't load root bit maps", NO_NUM); } /* This file contains a collection of miscellaneous procedures. Some of them * perform simple system calls. Some others do a little part of system calls * that are mostly performed by the Memory Manager. * * The entry points into this file are * do_dup: perform the DUP system call * do_sync: perform the SYNC system call * do_fork: adjust the tables after MM has performed a FORK system call * do_exit: a process has exited; note that in the tables * do_set: set uid or gid for some process * do_revive: revive a process that was waiting for something (e.g. TTY) */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "dev.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" #include "super.h" /*===========================================================================* * do_dup * *===========================================================================*/ PUBLIC int do_dup() { /* Perform the dup(fd) or dup(fd,fd2) system call. */ register int rfd; register struct fproc *rfp; struct filp *dummy; int r; extern struct filp *get_filp(); /* Is the file descriptor valid? */ rfd = fd & ~DUP_MASK; /* kill off dup2 bit, if on */ rfp = fp; if (get_filp(rfd) == NIL_FILP) return(err_code); /* Distinguish between dup and dup2. */ if (fd == rfd) { /* bit not on */ /* dup(fd) */ if ( (r = get_fd(0, &fd2, &dummy)) != OK) return(r); } else { /* dup2(fd, fd2) */ if (fd2 < 0 || fd2 >= NR_FDS) return(EBADF); if (rfd == fd2) return(fd2); /* ignore the call: dup2(x, x) */ fd = fd2; /* prepare to close fd2 */ do_close(); /* cannot fail */ } /* Success. Set up new file descriptors. */ rfp->fp_filp[fd2] = rfp->fp_filp[rfd]; rfp->fp_filp[fd2]->filp_count++; return(fd2); } /*===========================================================================* * do_sync * *===========================================================================*/ PUBLIC int do_sync() { /* Perform the sync() system call. Flush all the tables. */ register struct inode *rip; register struct buf *bp; register struct super_block *sp; dev_nr d; extern real_time clock_time(); extern struct super_block *get_super(); /* The order in which the various tables are flushed is critical. The * blocks must be flushed last, since rw_inode() and rw_super() leave their * results in the block cache. */ /* Update the time in the root super_block. */ sp = get_super(ROOT_DEV); sp->s_time = clock_time(); if (sp->s_rd_only == FALSE) sp->s_dirt = DIRTY; /* Write all the dirty inodes to the disk. */ for (rip = &inode[0]; rip < &inode[NR_INODES]; rip++) if (rip->i_count > 0 && rip->i_dirt == DIRTY) rw_inode(rip, WRITING); /* Write all the dirty super_blocks to the disk. */ for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) if (sp->s_dev != NO_DEV && sp->s_dirt == DIRTY) rw_super(sp, WRITING); /* Write all the dirty blocks to the disk. First do drive 0, then the rest. * This avoids starting drive 0, then starting drive 1, etc. */ for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) { d = bp->b_dev; if (d != NO_DEV && bp->b_dirt == DIRTY && ((d>>MINOR) & BYTE) == 0) rw_block(bp, WRITING); } for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) { d = bp->b_dev; if (d != NO_DEV && bp->b_dirt == DIRTY && ((d>>MINOR) & BYTE) != 0) rw_block(bp, WRITING); } return(OK); /* sync() can't fail */ } /*===========================================================================* * do_fork * *===========================================================================*/ PUBLIC int do_fork() { /* Perform those aspects of the fork() system call that relate to files. * In particular, let the child inherit its parents file descriptors. * The parent and child parameters tell who forked off whom. The file * system uses the same slot numbers as the kernel. Only MM makes this call. */ register struct fproc *cp; register char *sptr, *dptr; int i; /* Only MM may make this call directly. */ if (who != MM_PROC_NR) return(ERROR); /* Copy the parent's fproc struct to the child. */ sptr = (char *) &fproc[parent]; /* pointer to parent's 'fproc' struct */ dptr = (char *) &fproc[child]; /* pointer to child's 'fproc' struct */ i = sizeof(struct fproc); /* how many bytes to copy */ while (i--) *dptr++ = *sptr++; /* fproc[child] = fproc[parent] */ /* Increase the counters in the 'filp' table. */ cp = &fproc[child]; for (i = 0; i < NR_FDS; i++) if (cp->fp_filp[i] != NIL_FILP) cp->fp_filp[i]->filp_count++; /* Record the fact that both root and working dir have another user. */ dup_inode(cp->fp_rootdir); dup_inode(cp->fp_workdir); return(OK); } /*===========================================================================* * do_exit * *===========================================================================*/ PUBLIC int do_exit() { /* Perform the file system portion of the exit(status) system call. */ register int i, exitee; /* Only MM may do the EXIT call directly. */ if (who != MM_PROC_NR) return(ERROR); /* Nevertheless, pretend that the call came from the user. */ fp = &fproc[slot1]; /* get_filp() needs 'fp' */ exitee = slot1; if (fp->fp_suspended == SUSPENDED) { if (fp->fp_task == XPIPE) susp_count--; pro = exitee; do_unpause(); fp->fp_suspended = NOT_SUSPENDED; } /* Loop on file descriptors, closing any that are open. */ for (i=0; i < NR_FDS; i++) { fd = i; do_close(); } /* Release root and working directories. */ put_inode(fp->fp_rootdir); put_inode(fp->fp_workdir); return(OK); } /*===========================================================================* * do_set * *===========================================================================*/ PUBLIC int do_set() { /* Set uid or gid field. */ register struct fproc *tfp; /* Only MM may make this call directly. */ if (who != MM_PROC_NR) return(ERROR); tfp = &fproc[slot1]; if (fs_call == SETUID) { tfp->fp_realuid = (uid) real_user_id; tfp->fp_effuid = (uid) eff_user_id; } if (fs_call == SETGID) { tfp->fp_effgid = (gid) eff_grp_id; tfp->fp_realgid = (gid) real_grp_id; } return(OK); } /*===========================================================================* * do_revive * *===========================================================================*/ PUBLIC int do_revive() { /* A task, typically TTY, has now gotten the characters that were needed for a * previous read. The process did not get a reply when it made the call. * Instead it was suspended. Now we can send the reply to wake it up. This * business has to be done carefully, since the incoming message is from * a task (to which no reply can be sent), and the reply must go to a process * that blocked earlier. The reply to the caller is inhibited by setting the * 'dont_reply' flag, and the reply to the blocked process is done explicitly * in revive(). */ if (who > 0) return(EPERM); revive(m.REP_PROC_NR, m.REP_STATUS); dont_reply = TRUE; /* don't reply to the TTY task */ return(OK); } e/* This file performs the MOUNT and UMOUNT system calls. * * The entry points into this file are * do_mount: perform the MOUNT system call * do_umount: perform the UMOUNT system call */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" #include "super.h" /*===========================================================================* * do_mount * *===========================================================================*/ PUBLIC int do_mount() { /* Perform the mount(name, mfile, rd_only) system call. */ register struct inode *rip, *root_ip; struct super_block *xp, *sp; dev_nr dev; mask_bits bits; int r, found, loaded; extern struct inode *get_inode(), *eat_path(); extern dev_nr name_to_dev(); /* Only the super-user may do MOUNT. */ if (!super_user) return(EPERM); /* If 'name' is not for a block special file, return error. */ if (fetch_name(name1, name1_length, M1) != OK) return(err_code); if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code); /* Scan super block table to see if dev already mounted & find a free slot.*/ sp = NIL_SUPER; found = FALSE; for (xp = &super_block[0]; xp < &super_block[NR_SUPERS]; xp++) { if (xp->s_dev == dev) found = TRUE; /* is it mounted already? */ if (xp->s_dev == NO_DEV) sp = xp; /* record free slot */ } if (found) return(EBUSY); /* already mounted */ if (sp == NIL_SUPER) return(ENFILE); /* no super block available */ /* Fill in the super block. */ sp->s_dev = dev; /* rw_super() needs to know which dev */ rw_super(sp, READING); sp->s_dev = dev; /* however, rw_super() overwrites s_dev */ /* Make a few basic checks to see if super block looks reasonable. */ if (sp->s_magic != SUPER_MAGIC || sp->s_ninodes < 1 || sp->s_nzones < 1 || sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1) { sp->s_dev = NO_DEV; return(EINVAL); } /* Now get the inode of the file to be mounted on. */ if (fetch_name(name2, name2_length, M1) != OK) { sp->s_dev = NO_DEV; return(err_code); } if ( (rip = eat_path(user_path)) == NIL_INODE) { sp->s_dev = NO_DEV; return(err_code); } /* It may not be busy. */ r = OK; if (rip->i_count > 1) r = EBUSY; /* It may not be special. */ bits = rip->i_mode & I_TYPE; if (bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR; /* Get the root inode of the mounted file system. */ root_ip = NIL_INODE; /* if 'r' not OK, make sure this is defined */ if (r == OK) { if ( (root_ip = get_inode(dev, ROOT_INODE)) == NIL_INODE) r = err_code; } if (root_ip != NIL_INODE && root_ip->i_mode == 0) r = EINVAL; /* Load the i-node and zone bit maps from the new device. */ loaded = FALSE; if (r == OK) { if (load_bit_maps(dev) != OK) r = ENFILE; /* load bit maps */ loaded = TRUE; } /* File types of 'rip' and 'root_ip' may not conflict. */ if ( (r == OK) && ((rip->i_mode & I_TYPE) == I_DIRECTORY) && ((root_ip->i_mode & I_TYPE) != I_DIRECTORY)) r = ENOTDIR; /* If error, return the super block and both inodes; release the maps. */ if (r != OK) { put_inode(rip); put_inode(root_ip); if (loaded) unload_bit_maps(dev); do_sync(); invalidate(dev); sp->s_dev = NO_DEV; return(r); } /* Nothing else can go wrong. Perform the mount. */ rip->i_mount = I_MOUNT; /* this bit says the inode is mounted on */ sp->s_imount = rip; sp->s_isup = root_ip; sp->s_rd_only = rd_only; return(OK); } /*===========================================================================* * do_umount * *===========================================================================*/ PUBLIC int do_umount() { /* Perform the umount(name) system call. */ register struct inode *rip; struct super_block *sp, *sp1; dev_nr dev; int count; extern dev_nr name_to_dev(); /* Only the super-user may do UMOUNT. */ if (!super_user) return(EPERM); /* If 'name' is not for a block special file, return error. */ if (fetch_name(name, name_length, M3) != OK) return(err_code); if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code); /* See if the mounted device is busy. Only 1 inode using it should be * open -- the root inode -- and that inode only 1 time. */ count = 0; for (rip = &inode[0]; rip< &inode[NR_INODES]; rip++) if (rip->i_count > 0 && rip->i_dev == dev) count += rip->i_count; if (count > 1) return(EBUSY); /* can't umount a busy file system */ /* Find the super block. */ sp = NIL_SUPER; for (sp1 = &super_block[0]; sp1 < &super_block[NR_SUPERS]; sp1++) { if (sp1->s_dev == dev) { sp = sp1; break; } } /* Release the bit maps, sync the disk, and invalidate cache. */ if (sp != NIL_SUPER) if (unload_bit_maps(dev) != OK) panic("do_umount", NO_NUM); do_sync(); /* force any cached blocks out of memory */ invalidate(dev); /* invalidate cache entries for this dev */ if (sp == NIL_SUPER) return(EINVAL); /* Finish off the unmount. */ sp->s_imount->i_mount = NO_MOUNT; /* inode returns to normal */ put_inode(sp->s_imount); /* release the inode mounted on */ put_inode(sp->s_isup); /* release the root inode of the mounted fs */ sp->s_imount = NIL_INODE; sp->s_dev = NO_DEV; return(OK); } /*===========================================================================* * name_to_dev * *===========================================================================*/ PRIVATE dev_nr name_to_dev(path) char *path; /* pointer to path name */ { /* Convert the block special file 'path' to a device number. If 'path' * is not a block special file, return error code in 'err_code'. */ register struct inode *rip; register dev_nr dev; extern struct inode *eat_path(); /* If 'path' can't be opened, give up immediately. */ if ( (rip = eat_path(path)) == NIL_INODE) return(NO_DEV); /* If 'path' is not a block special file, return error. */ if ( (rip->i_mode & I_TYPE) != I_BLOCK_SPECIAL) { err_code = ENOTBLK; put_inode(rip); return(NO_DEV); } /* Extract the device number. */ dev = (dev_nr) rip->i_zone[0]; put_inode(rip); return(dev); } /* This file contains the procedures for creating, opening, closing, and * seeking on files. * * The entry points into this file are * do_creat: perform the CREAT system call * do_mknod: perform the MKNOD system call * do_open: perform the OPEN system call * do_close: perform the CLOSE system call * do_lseek: perform the LSEEK system call */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" PRIVATE char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0}; /*===========================================================================* * do_creat * *===========================================================================*/ PUBLIC int do_creat() { /* Perform the creat(name, mode) system call. */ register struct inode *rip; register int r; register mask_bits bits; struct filp *fil_ptr; int file_d; extern struct inode *new_node(); /* See if name ok and file descriptor and filp slots are available. */ if (fetch_name(name, name_length, M3) != OK) return(err_code); if ( (r = get_fd(W_BIT, &file_d, &fil_ptr)) != OK) return(r); /* Create a new inode by calling new_node(). */ bits = I_REGULAR | (mode & ALL_MODES & fp->fp_umask); rip = new_node(user_path, bits, NO_ZONE); r = err_code; if (r != OK && r != EEXIST) return(r); /* At this point two possibilities exist: the given path did not exist * and has been created, or it pre-existed. In the later case, truncate * if possible, otherwise return an error. */ if (r == EEXIST) { /* File exists already. */ switch (rip->i_mode & I_TYPE) { case I_REGULAR: /* truncate regular file */ if ( (r = forbidden(rip, W_BIT, 0)) == OK) truncate(rip); break; case I_DIRECTORY: /* can't truncate directory */ r = EISDIR; break; case I_CHAR_SPECIAL: /* special files are special */ case I_BLOCK_SPECIAL: if ( (r = forbidden(rip, W_BIT, 0)) != OK) break; r = dev_open( (dev_nr) rip->i_zone[0], W_BIT); break; } } /* If error, return inode. */ if (r != OK) { put_inode(rip); return(r); } /* Claim the file descriptor and filp slot and fill them in. */ fp->fp_filp[file_d] = fil_ptr; fil_ptr->filp_count = 1; fil_ptr->filp_ino = rip; return(file_d); } /*===========================================================================* * do_mknod * *===========================================================================*/ PUBLIC int do_mknod() { /* Perform the mknod(name, mode, addr) system call. */ register mask_bits bits; if (!super_user) return(EPERM); /* only super_user may make nodes */ if (fetch_name(name1, name1_length, M1) != OK) return(err_code); bits = (mode & I_TYPE) | (mode & ALL_MODES & fp->fp_umask); put_inode(new_node(user_path, bits, (zone_nr) addr)); return(err_code); } /*===========================================================================* * new_node * *===========================================================================*/ PRIVATE struct inode *new_node(path, bits, z0) char *path; /* pointer to path name */ mask_bits bits; /* mode of the new inode */ zone_nr z0; /* zone number 0 for new inode */ { /* This function is called by do_creat() and do_mknod(). In both cases it * allocates a new inode, makes a directory entry for it on the path 'path', * and initializes it. It returns a pointer to the inode if it can do this; * err_code is set to OK or EEXIST. If it can't, it returns NIL_INODE and * 'err_code' contains the appropriate message. */ register struct inode *rlast_dir_ptr, *rip; register int r; char string[NAME_SIZE]; extern struct inode *alloc_inode(), *advance(), *last_dir(); /* See if the path can be opened down to the last directory. */ if ((rlast_dir_ptr = last_dir(path, string)) == NIL_INODE) return(NIL_INODE); /* The final directory is accessible. Get final component of the path. */ rip = advance(rlast_dir_ptr, string); if ( rip == NIL_INODE && err_code == ENOENT) { /* Last path component does not exist. Make new directory entry. */ if ( (rip = alloc_inode(rlast_dir_ptr->i_dev, bits)) == NIL_INODE) { /* Can't creat new inode: out of inodes. */ put_inode(rlast_dir_ptr); return(NIL_INODE); } /* Force inode to the disk before making directory entry to make * the system more robust in the face of a crash: an inode with * no directory entry is much better than the opposite. */ rip->i_nlinks++; rip->i_zone[0] = z0; rw_inode(rip, WRITING); /* force inode to disk now */ /* New inode acquired. Try to make directory entry. */ if ((r = search_dir(rlast_dir_ptr, string, &rip->i_num,ENTER)) != OK) { put_inode(rlast_dir_ptr); rip->i_nlinks--; /* pity, have to free disk inode */ rip->i_dirt = DIRTY; /* dirty inodes are written out */ put_inode(rip); /* this call frees the inode */ err_code = r; return(NIL_INODE); } } else { /* Either last component exists, or there is some problem. */ if (rip != NIL_INODE) r = EEXIST; else r = err_code; } /* Return the directory inode and exit. */ put_inode(rlast_dir_ptr); err_code = r; return(rip); } /*===========================================================================* * do_open * *===========================================================================*/ PUBLIC int do_open() { /* Perform the open(name, mode) system call. */ register struct inode *rip; struct filp *fil_ptr; register int r; register mask_bits bits; int file_d; extern struct inode *eat_path(); /* See if file descriptor and filp slots are available. The variable * 'mode' is 0 for read, 1 for write, 2 for read+write. The variable * 'bits' needs to be R_BIT, W_BIT, and R_BIT|W_BIT respectively. */ if (mode < 0 || mode > 2) return(EINVAL); if (fetch_name(name, name_length, M3) != OK) return(err_code); bits = (mask_bits) mode_map[mode]; if ( (r = get_fd(bits, &file_d, &fil_ptr)) != OK) return(r); /* Scan path name. */ if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); if ((r = forbidden(rip, bits, 0)) != OK) { put_inode(rip); /* can't open: protection violation */ return(r); } /* Opening regular files, directories and special files are different. */ switch (rip->i_mode & I_TYPE) { case I_DIRECTORY: if (bits & W_BIT) { put_inode(rip); return(EISDIR); } break; case I_CHAR_SPECIAL: /* Assume that first open of char special file is controlling tty. */ if (fp->fs_tty == 0) fp->fs_tty = (dev_nr) rip->i_zone[0]; dev_open((dev_nr) rip->i_zone[0], (int) bits); break; case I_BLOCK_SPECIAL: dev_open((dev_nr) rip->i_zone[0], (int) bits); break; } /* Claim the file descriptor and filp slot and fill them in. */ fp->fp_filp[file_d] = fil_ptr; fil_ptr->filp_count = 1; fil_ptr->filp_ino = rip; return(file_d); } /*===========================================================================* * do_close * *===========================================================================*/ PUBLIC int do_close() { /* Perform the close(fd) system call. */ register struct filp *rfilp; register struct inode *rip; int rw; int mode_word; extern struct filp *get_filp(); /* First locate the inode that belongs to the file descriptor. */ if ( (rfilp = get_filp(fd)) == NIL_FILP) return(err_code); rip = rfilp->filp_ino; /* 'rip' points to the inode */ /* Check to see if the file is special. */ mode_word = rip->i_mode & I_TYPE; if (mode_word == I_CHAR_SPECIAL || mode_word == I_BLOCK_SPECIAL) { if (mode_word == I_BLOCK_SPECIAL) { /* Invalidate cache entries unless special is mounted or ROOT.*/ do_sync(); /* purge cache */ if (mounted(rip) == FALSE) invalidate((dev_nr) rip->i_zone[0]); } dev_close((dev_nr) rip->i_zone[0]); } /* If the inode being closed is a pipe, release everyone hanging on it. */ if (rfilp->filp_ino->i_pipe) { rw = (rfilp->filp_mode & R_BIT ? WRITE : READ); release(rfilp->filuwxp_ino, rw, NR_PROCS); } /* If a write has been done, the inode is already marked as DIRTY. */ if (--rfilp->filp_count == 0) put_inode(rfilp->filp_ino); fp->fp_filp[fd] = NIL_FILP; return(OK); } /*===========================================================================* * do_lseek * *===========================================================================*/ PUBLIC int do_lseek() { /* Perform the lseek(ls_fd, offset, whence) system call. */ register struct filp *rfilp; register file_pos pos; extern struct filp *get_filp(); /* Check to see if the file descriptor is valid. */ if ( (rfilp = get_filp(ls_fd)) == NIL_FILP) return(err_code); /* No lseek on pipes. */ if (rfilp->filp_ino->i_pipe == I_PIPE) return(ESPIPE); /* The value of 'whence' determines the algorithm to use. */ switch(whence) { case 0: pos = offset; break; case 1: pos = rfilp->filp_pos + offset; break; case 2: pos = rfilp->filp_ino->i_size + offset; break; default: return(EINVAL); } if (pos < (file_pos) 0) return(EINVAL); rfilp->filp_ino->i_seek = ISEEK; /* inhibit read ahead */ rfilp->filp_pos = pos; reply_l1 = pos; /* insert the long into the output message */ return(OK); } /* This file deals with the suspension and revival of processes. A process can * be suspended because it wants to read or write from a pipe and can't, or * because it wants to read or write from a special file and can't. When a * process can't continue it is suspended, and revived later when it is able * to continue. * * The entry points into this file are * do_pipe: perform the PIPE system call * pipe_check: check to see that a read or write on a pipe is feasible now * suspend: suspend a process that cannot do a requested read or write * release: check to see if a suspended process can be released and do it * revive: mark a suspended process as able to run again * do_unpause: a signal has been sent to a process; see if it suspended */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "../h/signal.h" #include "const.h" #include "type.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" PRIVATE message mess; /*===========================================================================* * do_pipe * *===========================================================================*/ PUBLIC int do_pipe() { /* Perform the pipe(fil_des) system call. */ register struct fproc *rfp; register struct inode *rip; int r; dev_nr device; struct filp *fil_ptr0, *fil_ptr1; int fil_des[2]; /* reply goes here */ extern struct inode *alloc_inode(); /* Acquire two file descriptors. */ rfp = fp; if ( (r = get_fd(R_BIT, &fil_des[0], &fil_ptr0)) != OK) return(r); rfp->fp_filp[fil_des[0]] = fil_ptr0; fil_ptr0->filp_count = 1; if ( (r = get_fd(W_BIT, &fil_des[1], &fil_ptr1)) != OK) { rfp->fp_filp[fil_des[0]] = NIL_FILP; fil_ptr0->filp_count = 0; return(r); } rfp->fp_filp[fil_des[1]] = fil_ptr1; fil_ptr1->filp_count = 1; /* Make the inode in the current working directory. */ device = rfp->fp_workdir->i_dev; /* inode dev is same as working dir */ if ( (rip = alloc_inode(device, I_REGULAR)) == NIL_INODE) { rfp->fp_filp[fil_des[0]] = NIL_FILP; fil_ptr0->filp_count = 0; rfp->fp_filp[fil_des[1]] = NIL_FILP; fil_ptr1->filp_count = 0; return(err_code); } rip->i_pipe = I_PIPE; fil_ptr0->filp_ino = rip; dup_inode(rip); /* for double usage */ fil_ptr1->filp_ino = rip; rw_inode(rip, WRITING); /* mark inode as allocated */ reply_i1 = fil_des[0]; reply_i2 = fil_des[1]; return(OK); } /*===========================================================================* * pipe_check * *===========================================================================*/ PUBLIC int pipe_check(rip, rw_flag, virgin, bytes, position) register struct inode *rip; /* the inode of the pipe */ int rw_flag; /* READING or WRITING */ int virgin; /* 1 if no data transferred yet, else 0 */ register int bytes; /* bytes to be read or written (all chunks) */ register file_pos *position; /* pointer to current file position */ { /* Pipes are a little different. If a process reads from an empty pipe for * which a writer still exists, suspend the reader. If the pipe is empty * and there is no writer, return 0 bytes. If a process is writing to a * pipe and no one is reading from it, give a broken pipe error. */ extern struct filp *find_filp(); /* If reading, check for empty pipe. */ if (rw_flag == READING) { if (*position >= rip->i_size) { /* Process is reading from an empty pipe. */ if (find_filp(rip, W_BIT) != NIL_FILP) { /* Writer exists; suspend rdr if no data already read.*/ if (virgin) suspend(XPIPE); /* block reader */ /* If need be, activate sleeping writer. */ if (susp_count > 0) release(rip, WRITE, 1); } return(0); } } else { /* Process is writing to a pipe. */ if (bytes > PIPE_SIZE) return(EFBIG); if (find_filp(rip, R_BIT) == NIL_FILP) { /* Tell MM to generate a SIGPIPE signal. */ mess.m_type = KSIG; mess.PROC1 = fp - fproc; mess.SIG_MAP = 1 << (SIGPIPE - 1); send(MM_PROC_NR, &mess); return(EPIPE); } if (*position + bytes > PIPE_SIZE) { suspend(XPIPE); /* stop writer -- pipe full */ return(0); } /* Writing to an empty pipe. Search for suspended reader. */ if (*position == 0) release(rip, READ, 1); } return(1); } /*===========================================================================* * suspend * *===========================================================================*/ PUBLIC suspend(task) int task; /* who is proc waiting for? (PIPE = pipe) */ { /* Take measures to suspend the processing of the present system call. * Store the parameters to be used upon resuming in the process table. * (Actually they are not used when a process is waiting for an I/O device, * but they are needed for pipes, and it is not worth making the distinction.) */ if (task == XPIPE) susp_count++; /* count procs suspended on pipe */ fp->fp_suspended = SUSPENDED; fp->fp_fd = fd << 8 | fs_call; fp->fp_buffer = buffer; fp->fp_nbytes = nbytes; fp->fp_task = -task; dont_reply = TRUE; /* do not send caller a reply message now */ } /*===========================================================================* * release * *===========================================================================*/ PUBLIC release(ip, call_nr, count) register struct inode *ip; /* inode of pipe */ int call_nr; /* READ or WRITE */ int count; /* max number of processes to release */ { /* Check to see if any process is hanging on the pipe whose inode is in 'ip'. * If one is, and it was trying to perform the call indicated by 'call_nr' * (READ or WRITE), release it. */ register struct fproc *rp; /* Search the proc table. */ for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) { if (rp->fp_suspended == SUSPENDED && (rp->fp_fd & BYTE) == call_nr && rp->fp_filp[rp->fp_fd>>8]->filp_ino == ip) { revive(rp - fproc, 0); susp_count--; /* keep track of who is suspended */ if (--count == 0) return; } } } /*===========================================================================* * revive * *===========================================================================*/ PUBLIC revive(proc_nr, bytes) int proc_nr; /* process to revive */ int bytes; /* if hanging on task, how many bytes read */ { /* Revive a previously blocked process. When a process hangs on tty, this * is the way it is eventually released. */ register struct fproc *rfp; if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("revive err", proc_nr); rfp = &fproc[proc_nr]; if (rfp->fp_suspended == NOT_SUSPENDED || rfp->fp_revived == REVIVING)return; /* The 'reviving' flag only applies to pipes. Processes waiting for TTY get * a message right away. The revival process is different for TTY and pipes. * For TTY revival, the work is already done, for pipes it is not: the proc * must be restarted so it can try again. */ if (rfp->fp_task == XPIPE) { /* Revive a process suspended on a pipe. */ rfp->fp_revived = REVIVING; reviving++; /* process was waiting on pipe */ } else { /* Revive a process suspended on TTY or other device. */ rfp->fp_suspended = NOT_SUSPENDED; rfp->fp_nbytes = bytes; /* pretend it only wants what there is */ reply(proc_nr, bytes); /* unblock the process */ } } /*===========================================================================* * do_unpause * *===========================================================================*/ PUBLIC int do_unpause() { /* A signal has been sent to a user who is paused on the file system. * Abort the system call with the EINTR error message. */ register struct fproc *rfp; int proc_nr, task, fild; struct filp *f; dev_nr dev; extern struct filp *get_filp(); if (who > MM_PROC_NR) return(EPERM); proc_nr = pro; if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("unpause err 1", proc_nr); rfp = &fproc[proc_nr]; if (rfp->fp_suspended == NOT_SUSPENDED) return(OK); task = -rfp->fp_task; if (task != XPIPE) { fild = (rfp->fp_fd >> 8) & BYTE; /* extract file descripto€‚r */ if (fild < 0 || fild >= NR_FDS) panic("unpause err 2", NO_NUM); f = rfp->fp_filp[fild]; dev = f->filp_ino->i_zone[0]; /* device on which proc is hanging */ mess.TTY_LINE = (dev >> MINOR) & BYTE; mess.PROC_NR = proc_nr; mess.m_type = CANCEL; if (sendrec(task, &mess) != OK) panic("unpause err 3", NO_NUM); while (mess.REP_PROC_NR != proc_nr) { revive(mess.REP_PROC_NR, mess.REP_STATUS); if (receive(task, &m) != OK) panic("unpause err 4", NO_NUM); } revive(proc_nr, EINTR); /* signal interrupted call */ } return(OK); } /* This file deals with protection in the file system. It contains the code * for four system calls that relate to protection. * * The entry points into this file are * do_chmod: perform the CHMOD system call * do_chown: perform the CHOWN system call * do_umask: perform the UMASK system call * do_access: perform the ACCESS system call * forbidden: check to see if a given access is allowed on a given inode */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" #include "super.h" /*===========================================================================* * do_chmod * *===========================================================================*/ PUBLIC int do_chmod() { /* Perform the chmod(name, mode) system call. */ register struct inode *rip; register int r; extern struct inode *eat_path(); /* Temporarily open the file. */ if (fetch_name(name, name_length, M3) != OK) return(err_code); if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); /* Only the owner or the super_user may change the mode of a file. * No one may change the mode of a file on a read-only file system. */ if (rip->i_uid != fp->fp_effuid && !super_user) r = EPERM; else r = read_only(rip); /* If error, return inode. */ if (r != OK) { put_inode(rip); return(r); } /* Now make the change. */ rip->i_mode = (rip->i_mode & ~ALL_MODES) | (mode & ALL_MODES); rip->i_dirt = DIRTY; put_inode(rip); return(OK); } /*===========================================================================* * do_chown * *===========================================================================*/ PUBLIC int do_chown() { /* Perform the chown(name, owner, group) system call. */ register struct inode *rip; register int r; extern struct inode *eat_path(); /* Only the super_user may perform the chown() call. */ if (!super_user) return(EPERM); /* Temporarily open the file. */ if (fetch_name(name1, name1_length, M1) != OK) return(err_code); if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); /* Not permitted to change the owner of a file on a read-only file sys. */ r = read_only(rip); if (r == OK) { rip->i_uid = owner; rip->i_gid = group; rip->i_dirt = DIRTY; } put_inode(rip); return(r); } /*===========================================================================* * do_umask * *===========================================================================*/ PUBLIC int do_umask() { /* Perform the umask(co_mode) system call. */ register mask_bits r; r = ~fp->fp_umask; /* set 'r' to complement of old mask */ fp->fp_umask = ~(co_mode & RWX_MODES); return(r); /* return complement of old mask */ } /*===========================================================================* * do_access * *===========================================================================*/ PUBLIC int do_access() { /* Perform the access(name, mode) system call. */ struct inode *rip; register int r; extern struct inode *eat_path(); /* Temporarily open the file whose access is to be checked. */ if (fetch_name(name, name_length, M3) != OK) return(err_code); if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); /* Now check the permissions. */ r = forbidden(rip, (mask_bits) mode, 1); put_inode(rip); return(r); } /*===========================================================================* * forbidden * *===========================================================================*/ PUBLIC int forbidden(rip, access_desired, real_uid) register struct inode *rip; /* pointer to inode to be checked */ mask_bits access_desired; /* RWX bits */ int real_uid; /* set iff real uid to be tested */ { /* Given a pointer to an inode, 'rip', and the accessed desired, determine * if the access is allowed, and if not why not. The routine looks up the * caller's uid in the 'fproc' table. If the access is allowed, OK is returned * if it is forbidden, EACCES is returned. */ register mask_bits bits, perm_bits, xmask; int r, shift, test_uid, test_gid; /* Isolate the relevant rwx bits from the mode. */ bits = rip->i_mode; test_uid = (real_uid ? fp->fp_realuid : fp->fp_effuid); test_gid = (real_uid ? fp->fp_realgid : fp->fp_effgid); if (test_uid == SU_UID) { perm_bits = 07; } else { if (test_uid == rip->i_uid) shift = 6; /* owner */ else if (test_gid == rip->i_gid ) shift = 3; /* group */ else shift = 0; /* other */ perm_bits = (bits >> shift) & 07; } /* If access desired is not a subset of what is allowed, it is refused. */ r = OK; if ((perm_bits | access_desired) != perm_bits) r = EACCES; /* If none of the X bits are on, not even the super-user can execute it. */ xmask = (X_BIT << 6) | (X_BIT << 3) | X_BIT; /* all 3 X bits */ if ( (access_desired & X_BIT) && (bits & xmask) == 0) r = EACCES; /* Check to see if someone is trying to write on a file system that is * mounted read-only. */ if (r == OK) if (access_desired & W_BIT) r = read_only(rip); return(r); } /*===========================================================================* * read_only * *===========================================================================*/ PRIVATE int read_only(ip) struct inode *ip; /* ptr to inode whose file sys is to be cked */ { /* Check to see if the file system on which the inode 'ip' resides is mounted * read only. If so, return EROFS, else return OK. */ register struct super_block *sp; extern struct super_block *get_super(); sp = get_super(ip->i_dev); return(sp->s_rd_only ? EROFS : OK); } /* FS must occasionally print some message. It uses the standard library * routine printf(), which calls putc() and flush. Library * versions of these routines do printing by sending messages to FS. Here * obviously can't do that, so FS calls the TTY task directly. */ #include "../h/const.h" #include "../h/type.h" #include "../h/com.h" #define STDOUTPUT 1 /* file descriptor for standard output */ #define BUFSIZE 100 /* print buffer size */ PRIVATE int bufcount; /* # characters in the buffer */ PRIVATE char printbuf [BUFSIZE]; /* output is buffered here */ PRIVATE message putchmsg; /* used for message to TTY task */ /*===========================================================================* * putc * *===========================================================================*/ PUBLIC putc(c) char c; { if (c == 0) { flush(); return; } printbuf[bufcount++] = c; if (bufcount == BUFSIZE) flush(); if (c == '\n') flush(); } /*===========================================================================* * flush * *===========================================================================*/ PRIVATE flush() { /* Flush the print buffer. */ if (bufcount == 0) return; putchmsg.m_type = TTY_WRITE; putchmsg.PROC_NR = 1; putchmsg.TTY_LINE = 0; putchmsg.ADDRESS = printbuf; putchmsg.COUNT = bufcount; sendrec(TTY, &putchmsg); bufcount = 0; } /* This file contains the heart of the mechanism used to read (and write) * files. Read and write requests are split up into chunks that do not cross * block boundaries. Each chunk is then processed in turn. Reads on special * files are also detected and handled. * * The entry points into this file are * do_read: perform the READ system call by calling read_write * read_write: actually do the work of READ and WRITE * read_map: given an inode and file position, lookup its zone number * rw_user: call the kernel to read and write user space * read_ahead: manage the block read ahead business */ #include "../h/const.h" #include "../h/type.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" #include "super.h" #define FD_MASK 077 /* max file descriptor is 63 */ PRIVATE message umess; /* message for asking SYSTASK for user copy */ PUBLIC int rdwt_err; /* set to EIO if disk error occurs */ /*===========================================================================* * do_read * *===========================================================================*/ PUBLIC int do_read() { return(read_write(READING)); } /*===========================================================================* * read_write * *===========================================================================*/ PUBLIC int read_write(rw_flag) int rw_flag; /* READING or WRITING */ { /* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */ register struct inode *rip; register struct filp *f; register file_pos bytes_left, f_size; register unsigned off, cum_io; file_pos position; int r, chunk, virg, mode_word, usr, seg; struct filp *wf; extern struct super_block *get_super(); extern struct filp *find_filp(), *get_filp(); extern real_time clock_time(); /* MM loads segments by putting funny things in upper 10 bits of 'fd'. */ if (who == MM_PROC_NR && (fd & (~BYTE)) ) { usr = (fd >> 8) & BYTE; seg = (fd >> 6) & 03; fd &= FD_MASK; /* get rid of user and segment bits */ } else { usr = who; /* normal case */ seg = D; } /* If the file descriptor is valid, get the inode, size and mode. */ if (nbytes == 0) return(0); /* so char special files need not check for 0*/ if (who != MM_PROC_NR && nbytes < 0) return(EINVAL); /* only MM > 32K */ if ( (f = get_filp(fd)) == NIL_FILP) return(err_code); if ( ((f->filp_mode) & (rw_flag == READING ? R_BIT : W_BIT)) == 0) return(EBADF); position = f->filp_pos; if (position < (file_pos) 0) return(EINVAL); rip = f->filp_ino; f_size = rip->i_size; r = OK; cum_io = 0; virg = TRUE; mode_word = rip->i_mode & I_TYPE; if (mode_word == I_BLOCK_SPECIAL && f_size == 0) f_size = MAX_P_LONG; rdwt_err = OK; /* set to EIO if disk error occurs */ /* Check for character special files. */ if (mode_word == I_CHAR_SPECIAL) { if ((r = dev_io(rw_flag, (dev_nr) rip->i_zone[0], (long) position, nbytes, who, buffer)) >= 0) { cum_io = r; position += r; r = OK; } } else { if (rw_flag == WRITING && mode_word != I_BLOCK_SPECIAL) { /* Check in advance to see if file will grow too big. */ if (position > get_super(rip->i_dev)->s_max_size - nbytes ) return(EFBIG); /* Clear the zone containing present EOF if hole about * to be created. This is necessary because all unwritten * blocks prior to the EOF must read as zeros. */ if (position > f_size) clear_zone(rip, f_size, 0); } /* Pipes are a little different. Check. */ if (rip->i_pipe && (r = pipe_check(rip, rw_flag, virg, nbytes, &position)) <= 0) return(r); /* Split the transfer into chunks that don't span two blocks. */ while (nbytes != 0) { off = position % BLOCK_SIZE; /* offset within a block */ chunk = MIN(nbytes, BLOCK_SIZE - off); if (chunk < 0) chunk = BLOCK_SIZE - off; if (rw_flag == READING) { if ((bytes_left = f_size - position) <= 0) break; else if (chunk > bytes_left) chunk = bytes_left; } /* Read or write 'chunk' bytes. */ r=rw_chunk(rip, position, off, chunk, rw_flag, buffer, seg,usr); if (r != OK) break; /* EOF reached */ if (rdwt_err < 0) break; /* Update counters and pointers. */ buffer += chunk; /* user buffer address */ nbytes -= chunk; /* bytes yet to be read */ cum_io += chunk; /* bytes read so far */ position += chunk; /* position within the file */ virg = FALSE; /* tells pipe_check() that data has been copied */ } } /* On write, update file size and access time. */ if (rw_flag == WRITING) { if (mode_word != I_CHAR_SPECIAL && mode_word != I_BLOCK_SPECIAL && position > f_size) rip->i_size = position; rip->i_modtime = clock_time(); rip->i_dirt = DIRTY; } else { if (rip->i_pipe && position >= rip->i_size) { /* Reset pipe pointers. */ rip->i_size = 0; /* no data left */ position = 0; /* reset reader(s) */ if ( (wf = find_filp(rip, W_BIT)) != NIL_FILP) wf->filp_pos = 0; } } f->filp_pos = position; /* Check to see if read-ahead is called for, and if so, set it up. */ if (rw_flag == READING && rip->i_seek == NO_SEEK && position % BLOCK_SIZE == 0 && (mode_word == I_REGULAR || mode_word == I_DIRECTORY)) { rdahed_inode = rip; rdahedpos = position; } if (mode_word == I_REGULAR) rip->i_seek = NO_SEEK; if (rdwt_err != OK) r = rdwt_err; /* check for disk error */ if (rdwt_err == EOF) r = cum_io; return(r == OK ? cum_io : r); } /*===========================================================================* * rw_chunk * *===========================================================================*/ PRIVATE int rw_chunk(rip, position, off, chunk, rw_flag, buff, seg, usr) register struct inode *rip; /* pointer to inode for file to be rd/wr */ file_pos position; /* position within file to read or write */ unsigned off; /* off within the current block */ int chunk; /* number of bytes to read or write */ int rw_flag; /* READING or WRITING */ char *buff; /* virtual address of the user buffer */ int seg; /* T or D segment in user space */ int usr; /* which user process */ { /* Read or write (part of) a block. */ register struct buf *bp; register int r; int dir, n, block_spec; block_nr b; dev_nr dev; extern struct buf *get_block(), *new_block(); extern block_nr read_map(); block_spec = (rip->i_mode & I_TYPE) == I_BLOCK_SPECIAL; if (block_spec) { b = position/BLOCK_SIZE; dev = (dev_nr) rip->i_zone[0]; } else { b = read_map(rip, position); dev = rip->i_dev; } if (!block_spec && b == NO_BLOCK) { if (rw_flag == READING) { /* Reading from a nonexistent block. Must read as all zeros. */ bp = get_block(NO_DEV, NO_BLOCK, NORMAL); /* get a buffer */ zero_block(bp); } else { /* Writing to a nonexistent block. Create and enter in inode. */ if ((bp = new_block(rip, position)) == NIL_BUF)return(err_code); } } else { /* Normally an existing block to be partially overwritten is first read * in. However, a full block need not be read in. If it is already in * the cache, acquire it, otherwise just acquire a free buffer. */ n = (rw_flag == WRITING && chunk == BLOCK_SIZE ? NO_READ : NORMAL); if(rw_flag == WRITING && off == 0 && position >= rip->i_size) n=NO_READ; bp = get_block(dev, b, n); } /* In all cases, bp now points to a valid buffer. */ if (rw_flag == WRITING && chunk != BLOCK_SIZE && !block_spec && position >= rip->i_size && off == 0) zero_block(bp); dir = (rw_flag == READING ? TO_USER : FROM_USER); r = rw_user(seg, usr, (vir_bytes)buff, (vir_bytes)chunk, bp->b_data+off, dir); if (rw_flag == WRITING) bp->b_dirt = DIRTY; n = (off + chunk == BLOCK_SIZE ? FULL_DATA_BLOCK : PARTIAL_DATA_BLOCK); put_block(bp, n); return(r); } /*===========================================================================* * read_map * *===========================================================================*/ PUBLIC block_nr read_map(rip, position) register struct inode *rip; /* ptr to in’”•–—ode to map from */ file_pos position; /* position in file whose blk wanted */ { /* Given an inode and a position within the corresponding file, locate the * block (not zone) number in which that position is to be found and return it. */ register struct buf *bp; register zone_nr z; register block_nr b; register long excess, zone, block_pos; register int scale, boff; extern struct buf *get_block(); scale = scale_factor(rip); /* for block-zone conversion */ block_pos = position/BLOCK_SIZE; /* relative blk # in file */ zone = block_pos >> scale; /* position's zone */ boff = block_pos - (zone << scale); /* relative blk # within zone */ /* Is 'position' to be found in the inode itself? */ if (zone < NR_DZONE_NUM) { if ( (z = rip->i_zone[zone]) == NO_ZONE) return(NO_BLOCK); b = ((block_nr) z << scale) + boff; return(b); } /* It is not in the inode, so it must be single or double indirect. */ excess = zone - NR_DZONE_NUM; /* first NR_DZONE_NUM don't count */ if (excess < NR_INDIRECTS) { /* 'position' can be located via the single indirect block. */ z = rip->i_zone[NR_DZONE_NUM]; } else { /* 'position' can be located via the double indirect block. */ if ( (z = rip->i_zone[NR_DZONE_NUM+1]) == NO_ZONE) return(NO_BLOCK); excess -= NR_INDIRECTS; /* single indir doesn't count */ b = (block_nr) z << scale; bp = get_block(rip->i_dev, b, NORMAL); /* get double indirect block */ z = bp->b_ind[excess/NR_INDIRECTS]; /* z is zone # for single ind */ put_block(bp, INDIRECT_BLOCK); /* release double ind block */ excess = excess % NR_INDIRECTS; /* index into single ind blk */ } /* 'z' is zone number for single indirect block; 'excess' is index into it. */ if (z == NO_ZONE) return(NO_BLOCK); b = (block_nr) z << scale; bp = get_block(rip->i_dev, b, NORMAL); /* get single indirect block */ z = bp->b_ind[excess]; put_block(bp, INDIRECT_BLOCK); /* release single indirect blk */ if (z == NO_ZONE) return(NO_BLOCK); b = ((block_nr) z << scale) + boff; return(b); } /*===========================================================================* * rw_user * *===========================================================================*/ PUBLIC int rw_user(s, u, vir, bytes, buff, direction) int s; /* D or T space (stack is also D) */ int u; /* process number to r/w (usually = 'who') */ vir_bytes vir; /* virtual address to move to/from */ vir_bytes bytes; /* how many bytes to move */ char *buff; /* pointer to FS space */ int direction; /* TO_USER or FROM_USER */ { /* Transfer a block of data. Two options exist, depending on 'direction': * TO_USER: Move from FS space to user virtual space * FROM_USER: Move from user virtual space to FS space */ if (direction == TO_USER ) { /* Write from FS space to user space. */ umess.SRC_SPACE = D; umess.SRC_PROC_NR = FS_PROC_NR; umess.SRC_BUFFER = (long) buff; umess.DST_SPACE = s; umess.DST_PROC_NR = u; umess.DST_BUFFER = (long) vir; } else { /* Read from user space to FS space. */ umess.SRC_SPACE = s; umess.SRC_PROC_NR = u; umess.SRC_BUFFER = (long) vir; umess.DST_SPACE = D; umess.DST_PROC_NR = FS_PROC_NR; umess.DST_BUFFER = (long) buff; } umess.COPY_BYTES = (long) bytes; sys_copy(&umess); return(umess.m_type); } /*===========================================================================* * read_ahead * *===========================================================================*/ PUBLIC read_ahead() { /* Read a block into the cache before it is needed. */ register struct inode *rip; struct buf *bp; block_nr b; extern struct buf *get_block(); rip = rdahed_inode; /* pointer to inode to read ahead from */ rdahed_inode = NIL_INODE; /* turn off read ahead */ if ( (b = read_map(rip, rdahedpos)) == NO_BLOCK) return; /* at EOF */ bp = get_block(rip->i_dev, b, NORMAL); put_block(bp, PARTIAL_DATA_BLOCK); } /* This file contains the code for performing four system calls relating to * status and directories. * * The entry points into this file are * do_chdir: perform the CHDIR system call * do_chroot: perform the CHROOT system call * do_stat: perform the STAT system call * do_fstat: perform the FSTAT system call */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "../h/stat.h" #include "const.h" #include "type.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" /*===========================================================================* * do_chdir * *===========================================================================*/ PUBLIC int do_chdir() { /* Change directory. This function is also called by MM to simulate a chdir * in order to do EXEC, etc. */ register struct fproc *rfp; if (who == MM_PROC_NR) { rfp = &fproc[slot1]; put_inode(fp->fp_workdir); fp->fp_workdir = (cd_flag ? fp->fp_rootdir : rfp->fp_workdir); dup_inode(fp->fp_workdir); fp->fp_effuid = (cd_flag ? SUPER_USER : rfp->fp_effuid); return(OK); } /* Perform the chdir(name) system call. */ return change(&fp->fp_workdir, name, name_length); } /*===========================================================================* * do_chroot * *===========================================================================*/ PUBLIC int do_chroot() { /* Perform the chroot(name) system call. */ register int r; if (!super_user) return(EPERM); /* only su may chroot() */ r = change(&fp->fp_rootdir, name, name_length); return(r); } /*===========================================================================* * change * *===========================================================================*/ PRIVATE int change(iip, name_ptr, len) struct inode **iip; /* pointer to the inode pointer for the dir */ char *name_ptr; /* pointer to the directory name to change to */ int len; /* length of the directory name string */ { /* Do the actual work for chdir() and chroot(). */ struct inode *rip; register int r; extern struct inode *eat_path(); /* Try to open the new directory. */ if (fetch_name(name_ptr, len, M3) != OK) return(err_code); if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); /* It must be a directory and also be searchable. */ if ( (rip->i_mode & I_TYPE) != I_DIRECTORY) r = ENOTDIR; else r = forbidden(rip, X_BIT, 0); /* check if dir is searchable */ /* If error, return inode. */ if (r != OK) { put_inode(rip); return(r); } /* Everything is OK. Make the change. */ put_inode(*iip); /* release the old directory */ *iip = rip; /* acquire the new one */ return(OK); } /*===========================================================================* * do_stat * *===========================================================================*/ PUBLIC int do_stat() { /* Perform the stat(name, buf) system call. */ register struct inode *rip; register int r; extern struct inode *eat_path(); /* Both stat() and fstat() use the same routine to do the real work. That * routine expects an inode, so acquire it temporarily. */ if (fetch_name(name1, name1_length, M1) != OK) return(err_code); if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); r = stat_inode(rip, NIL_FILP, name2); /* actually do the work.*/ put_inode(rip); /* release the inode */ return(r); } /*===========================================================================* * do_fstat * *===========================================================================*/ PUBLIC int do_fstat() { /* Perform the fstat(fd, buf) system call. */ register struct filp *rfilp; extern struct filp *get_filp(); /* Is the file descriptor valid? */ if ( (rfilp = get_filp(fd)) == NIL_FILP) return(err_code); return(stat_inode(rfilp->filp_ino, rfilp, buffer)); } /*===========================================================================* * stat_inode * *===========================================================================*/ PRIVATE int stat_inode(rip, fil_ptr, user_addr) register struct inode *rip; /* pointer to inode to stat */ struct filp *fil_ptr; /* filp pointer, supplied by 'fstat' */ char *user_addr; /* user space address where stat buf goes */ { /* Common code for stat and fstat system calls. */ register struct stat *stp; struct stat statbuf; int r; vir_bytes v; /* Fill in the statbuf struct. */ stp = &statbuf; /* set up pointer to the buffer */ stp->st_dev = (int) rip->i_dev; stp->st_ino = rip->i_num; stp->st_mode = rip->i_mode; stp->st_nlink = rip->i_nlinks & BYTE; stp->st_uid = rip->i_uid; stp->st_gid = rip->i_gid & BYTE; stp->st_rdev = rip->i_zone[0]; stp->st_size = rip->i_size; if ( (rip->i_pipe == I_PIPE) && /* IF it is a pipe */ (fil_ptr != NIL_FILP) && /* AND it was fstat */ (fil_ptr->filp_mode == R_BIT)) /* on the reading end, */ stp->st_size -= fil_ptr->filp_pos; /* adjust the visible size. */ stp->st_atime = rip->i_modtime; stp->st_mtime = rip->i_modtime; stp->st_ctime = rip->i_modtime; /* Copy the struct to user space. */ v = (vir_bytes) user_addr; r = rw_user(D, who, v, (vir_bytes) sizeof statbuf, (char *) stp, TO_USER); return(r); } /* This file manages the super block table and the related data structures, * namely, the bit maps that keep track of which zones and which inodes are * allocated and which are free. When a new inode or zone is needed, the * appropriate bit map is searched for a free entry. * * The entry points into this file are * load_bit_maps: get the bit maps for the root or a newly mounted device * unload_bit_maps: write the bit maps back to disk after an UMOUNT * alloc_bit: somebody wants to allocate a zone or inode; find one * free_bit: indicate that a zone or inode is available for allocation * get_super: search the 'superblock' table for a device * mounted: tells if file inode is on mounted (or ROOT) file system * scale_factor: get the zone-to-block conversion factor for a device * rw_super: read or write a superblock */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "inode.h" #include "super.h" #define INT_BITS (sizeof(int)<<3) #define BIT_MAP_SHIFT 13 /* (log2 of BLOCK_SIZE) + 3; 13 for 1k blocks */ /*===========================================================================* * load_bit_maps * *===========================================================================*/ PUBLIC int load_bit_maps(dev) dev_nr dev; /* which device? */ { /* Load the bit map for some device into the cache and set up superblock. */ register int i; register struct super_block *sp; block_nr zbase; extern struct buf *get_block(); extern struct super_block *get_super(); sp = get_super(dev); /* get the superblock pointer */ if (bufs_in_use + sp->s_imap_blocks + sp->s_zmap_blocks >= NR_BUFS - 3) return(ERROR); /* insufficient buffers left for bit maps */ if (sp->s_imap_blocks > I_MAP_SLOTS || sp->s_zmap_blocks > ZMAP_SLOTS) panic("too many map blocks", NO_NUM); /* Load the inode map from the disk. */ for (i = 0; i < sp->s_imap_blocks; i++) sp->s_imap[i] = get_block(dev, SUPER_BLOCK + 1 + i, NORMAL); /* Load the zone map from the disk. */ zbase = SUPER_BLOCK + 1 + sp->s_imap_blocks; for (i = 0; i < sp->s_zmap_blocks; i++) sp->s_zmap[i] = get_block(dev, zbase + i, NORMAL); /* inodes 0 and 1, and zone 0 are never allocated. Mark them as busy. */ sp->s_imap[0]->b_int[0] |= 3; /* inodes 0, 1 busy */ sp->s_zmap[0]->b_int[0] |= 1; /* zone 0 busy */ bufs_in_use += sp->s_imap_blocks + sp->s_zmap_blocks; return(OK); } /*===========================================================================* * unload_bit_maps * *===========================================================================*/ PUBLIC unload_bit_maps(dev) dev_nr dev; /* which device is being unmounted? */ { /* Unload the bit maps so a device can be unmounted. */ register int i; register struct super_block *sp; struct super_block *get_super(); sp = get_super(dev); /* get the superblock pointer */ bufs_in_use -= sp->s_imap_blocks + sp->s_zmap_blocks; for (i = 0; i < sp->s_imap_blocks; i++) put_block(sp->s_imap[i], I_MAP_BLOCK); for (i = 0; i < sp->s_zmap_blocks; i++) put_block(sp->s_zmap[i], ZMAP_BLOCK); return(OK); } /*===========================================================================* * alloc_bit * *===========================================================================*/ PUBLIC bit_nr alloc_bit(map_ptr, map_bits, bit_blocks, origin) struct buf *map_ptr[]; /* pointer to array of bit block pointers */ bit_nr map_bits; /* how many bits are there in the bit map? */ unshort bit_blocks; /* how many blocks are there in the bit map? */ bit_nr origin; /* number of bit to start searching at */ { /* Allocate a bit from a bit map and return its bit number. */ register unsigned k; register int *wptr, *wlim; int i, a, b, w, o, block_count; struct buf *bp; /* Figure out where to start the bit search (depends on 'origin'). */ if (origin >= map_bits) origin = 0; /* for robustness */ b = origin >> BIT_MAP_SHIFT; o = origin - (b << BIT_MAP_SHIFT); w = o/INT_BITS; block_count = (w == 0 ? bit_blocks : bit_blocks + 1); /* The outer while loop iterates on the blocks of the map. The inner * while loop iterates on the words of a block. The for loop iterates * on the bits of a word. */ while (block_count--) { /* If need be, loop on all the blocks in the bit map. */ bp = map_ptr[b]; wptr = &bp->b_int[w]; wlim = &bp->b_int[INTS_PER_BLOCK]; while (wptr != wlim) { /* Loop on all the words of one of the bit map blocks. */ if ((k = (unsigned) *wptr) != (unsigned) ~0) { /* This word contains a free bit. Allocate it. */ for (i = 0; i < INT_BITS; i++) if (((k >> i) & 1) == 0) { a = i + (wptr - &bp->b_int[0])*INT_BITS + (b << BIT_MAP_SHIFT); /* If 'a' beyond map check other blks*/ if (a >= map_bits) { wptr = wlim - 1; break; } *wptr |= 1 << i; bp->b_dirt = DIRTY; return( (bit_nr) a); } } wptr++; /* examine next word in this bit map block */ } if (++b == bit_blocks) b = 0; /* we have wrapped around */ w = 0; } return(NO_BIT); /* no bit could be allocated */ } /*===========================================================================* * free_bit * *===========================================================================*/ PUBLIC free_bit(map_ptr, bit_returned) struct buf *map_ptr[]; /* pointer to array of bit block pointers */ bit_nr bit_returned; /* number of bit to insert into the map */ { /* Return a zone or inode by turning on its bitmap bit. */ int b, r, w, bit; struct buf *bp; b = bit_returned >> BIT_MAP_SHIFT; /* 'b' tells which block it is in */ r = bit_returned - (b << BIT_MAP_SHIFT); w = r/INT_BITS; /* 'w' tells which word it is in */ bit = r % INT_BITS; bp = map_ptr[b]; if (bp == NIL_BUF) return; if (((bp->b_int[w] >> bit)& 1)== 0) panic("freeing unused block or inode--check file sys",(int)bit_returned); bp->b_int[w] &= ~(1 << bit); /* turn the bit on */ bp->b_dirt = DIRTY; } /*===========================================================================* * get_super * *===========================================================================*/ PUBLIC struct super_block *get_super(dev) dev_nr dev; /* device number whose super_block is sought */ { /* Search the superblock table for this device. It is supposed to be there. */ register struct super_block *sp; for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) if (sp->s_dev == dev) return(sp); /* Search failed. Something wrong. */ panic("can't find superblock for device (in decimal)", (int) dev); } /*===========================================================================* * mounted * *===========================================================================*/ PUBLIC int mounted(rip) register struct inode *rip; /* pointer to inode */ { /* Report on whether the given inode is on a mounted (or ROOT) file system. */ register struct super_block *sp; register dev_nr dev; dev = (dev_nr) rip->i_zone[0]; if (dev == ROOT_DEV) return(TRUE); /* inode is on root file system */ for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) if (sp->s_dev == dev) return(TRUE); return(FALSE); } /*===========================================================================* * scale_factor * *===========================================================================*/ PUBLIC int scale_factor(ip) struct inode *ip; /* pointer to inode whose superblock needed */ { /* Return the scale factor used for converting blocks to zones. */ register struct super_block *sp; extern struct super_block *get_super(); sp = get_super(ip->i_dev); return(sp->s_log_zone_size); } /*===========================================================================* * rw_super * *===========================================================================*/ PUBLIC rw_super(sp, rw_flag) register struct super_block *sp; /* pointer to a superblock */ int rw_fЅЇlag; /* READING or WRITING */ { /* Read or write a superblock. */ register struct buf *bp; dev_nr dev; extern struct buf *get_block(); /* Check if this is a read or write, and do it. */ if (rw_flag == READING) { dev = sp->s_dev; /* save device; it will be overwritten by copy*/ bp = get_block(sp->s_dev, (block_nr) SUPER_BLOCK, NORMAL); copy( (char *) sp, bp->b_data, SUPER_SIZE); sp->s_dev = dev; /* restore device number */ } else { /* On a write, it is not necessary to go read superblock from disk. */ bp = get_block(sp->s_dev, (block_nr) SUPER_BLOCK, NO_READ); copy(bp->b_data, (char *) sp, SUPER_SIZE); bp->b_dirt = DIRTY; } sp->s_dirt = CLEAN; put_block(bp, ZUPER_BLOCK); } /* This file contains the table used to map system call numbers onto the * routines that perform them. */ #include "../h/const.h" #include "../h/type.h" #include "../h/stat.h" #include "const.h" #include "type.h" #include "dev.h" #undef EXTERN #define EXTERN #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "super.h" extern do_access(), do_chdir(), do_chmod(), do_chown(), do_chroot(); extern do_close(), do_creat(), do_dup(), do_exit(), do_fork(), do_fstat(); extern do_ioctl(), do_link(), do_lseek(), do_mknod(), do_mount(), do_open(); extern do_pipe(), do_read(), do_revive(), do_set(), do_stat(), do_stime(); extern do_sync(), do_time(), do_tims(), do_umask(), do_umount(), do_unlink(); extern do_unpause(), do_utime(), do_write(), no_call(), no_sys(); extern char fstack[]; char *stackpt = &fstack[FS_STACK_BYTES]; /* initial stack pointer */ int (*call_vector[NCALLS])() = { no_sys, /* 0 = unused */ do_exit, /* 1 = exit */ do_fork, /* 2 = fork */ do_read, /* 3 = read */ do_write, /* 4 = write */ do_open, /* 5 = open */ do_close, /* 6 = close */ no_sys, /* 7 = wait */ do_creat, /* 8 = creat */ do_link, /* 9 = link */ do_unlink, /* 10 = unlink */ no_sys, /* 11 = exec */ do_chdir, /* 12 = chdir */ do_time, /* 13 = time */ do_mknod, /* 14 = mknod */ do_chmod, /* 15 = chmod */ do_chown, /* 16 = chown */ no_sys, /* 17 = break */ do_stat, /* 18 = stat */ do_lseek, /* 19 = lseek */ no_sys, /* 20 = getpid */ do_mount, /* 21 = mount */ do_umount, /* 22 = umount */ do_set, /* 23 = setuid */ no_sys, /* 24 = getuid */ do_stime, /* 25 = stime */ no_sys, /* 26 = (ptrace)*/ no_sys, /* 27 = alarm */ do_fstat, /* 28 = fstat */ no_sys, /* 29 = pause */ do_utime, /* 30 = utime */ no_sys, /* 31 = (stty) */ no_sys, /* 32 = (gtty) */ do_access, /* 33 = access */ no_sys, /* 34 = (nice) */ no_sys, /* 35 = (ftime) */ do_sync, /* 36 = sync */ no_sys, /* 37 = kill */ no_sys, /* 38 = unused */ no_sys, /* 39 = unused */ no_sys, /* 40 = unused */ do_dup, /* 41 = dup */ do_pipe, /* 42 = pipe */ do_tims, /* 43 = times */ no_sys, /* 44 = (prof) */ no_sys, /* 45 = unused */ do_set, /* 46 = setgid */ no_sys, /* 47 = getgid */ no_sys, /* 48 = sig */ no_sys, /* 49 = unused */ no_sys, /* 50 = unused */ no_sys, /* 51 = (acct) */ no_sys, /* 52 = (phys) */ no_sys, /* 53 = (lock) */ do_ioctl, /* 54 = ioctl */ no_sys, /* 55 = unused */ no_sys, /* 56 = (mpx) */ no_sys, /* 57 = unused */ no_sys, /* 58 = unused */ no_sys, /* 59 = exece */ do_umask, /* 60 = umask */ do_chroot, /* 61 = chroot */ no_sys, /* 62 = unused */ no_sys, /* 63 = unused */ no_sys, /* 64 = KSIG: signals originating in the kernel */ do_unpause, /* 65 = UNPAUSE */ no_sys, /* 66 = BRK2 (used to tell MM size of FS,INIT) */ do_revive, /* 67 = REVIVE */ no_sys /* 68 = TASK_REPLY */ }; extern rw_dev(), rw_dev2(); /* The order of the entries here determines the mapping between major device * numbers and tasks. The first entry (major device 0) is not used. The * next entry is major device 1, etc. Character and block devices can be * intermixed at random. If this ordering is changed, BOOT_DEV and ROOT_DEV * must be changed to correspond to the new values. */ struct dmap dmap[] = { /* Open Read/Write Close Task # Device File ---- ---------- ----- ------- ------ ---- */ 0, 0, 0, 0, /* 0 = not used */ no_call, rw_dev, no_call, MEM, /* 1 = /dev/mem */ no_call, rw_dev, no_call, FLOPPY, /* 2 = /dev/fd0 */ no_call, rw_dev, no_call, WINCHESTER, /* 3 = /dev/hd0 */ no_call, rw_dev, no_call, TTY, /* 4 = /dev/tty0 */ no_call, rw_dev2, no_call, TTY, /* 5 = /dev/tty */ no_call, rw_dev, no_call, PRINTER /* 6 = /dev/lp */ }; int max_major = sizeof(dmap)/sizeof(struct dmap); /* This file takes care of those system calls that deal with time. * * The entry points into this file are * do_utime: perform the UTIME system call * do_time: perform the TIME system call * do_stime: perform the STIME system call * do_tims: perform the TIMES system call */ #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" PRIVATE message clock_mess; /*===========================================================================* * do_utime * *===========================================================================*/ PUBLIC int do_utime() { /* Perform the utime(name, timep) system call. */ register struct inode *rip; register int r; extern struct inode *eat_path(); /* Temporarily open the file. */ if (fetch_name(utime_file, utime_length, M1) != OK) return(err_code); if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); /* Only the owner of a file or the super_user can change its time. */ r = OK; if (rip->i_uid != fp->fp_effuid && !super_user) r = EPERM; if (r == OK) { rip->i_modtime = update_time; rip->i_dirt = DIRTY; } put_inode(rip); return(r); } /*===========================================================================* * do_time * *===========================================================================*/ PUBLIC int do_time() { /* Perform the time(tp) system call. */ extern real_time clock_time(); reply_l1 = clock_time(); /* return time in seconds */ return(OK); } /*===========================================================================* * do_stime * *===========================================================================*/ PUBLIC int do_stime() { /* Perform the stime(tp) system call. */ register int k; if (!super_user) return(EPERM); clock_mess.m_type = SET_TIME; clock_mess.NEW_TIME = (long) tp; if ( (k = sendrec(CLOCK, &clock_mess)) != OK) panic("do_stime error", k); return (OK); } /*===========================================================================* * do_tims * *===========================================================================*/ PUBLIC int do_tims() { /* Perform the times(buffer) system call. */ real_time t[4]; sys_times(who, t); reply_t1 = t[0]; reply_t2 = t[1]; reply_t3 = t[2]; reply_t4 = t[3]; return(OK); } /* This file contains a few general purpose utility routines. * * The entry points into this file are * clock_time: ask the clock task for the real time * cmp_string: compare two strings (e.g., while searching directory) * copy: copy a string * fetch_name: go get a path name from user space * no_sys: reject a system call that FS does not handle * panic: something awful has occurred; MINIX cannot continue */ #include "../h/const.h" #include "../h/type.h" #include "../h/com.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "param.h" #include "super.h" PRIVATE int panicking; /* inhibits recursive panics during sync */ PRIVATE message clock_mess; /*===========================================================================* * clock_time * *===========================================================================*/ PUBLIC real_time clock_time() { /* This routine returns the time in seconds since 1.1.1970. */ register int k; register struct super_block *sp; extern struct super_block *get_super(); clock_mess.m_type = GET_TIME; if ( (k = sendrec(CLOCK, &clock_mess)) != OK) panic("clock_time err", k); /* Since we now have the time, update the super block. It is almost free. */ sp = get_super(ROOT_DEV); sp->s_time = clock_mess.NEW_TIME; /* update super block time */ if (sp->s_rd_only == FALSE) sp->s_dirt = DIRTY; return (real_time) clock_mess.NEW_TIME; } /*===========================================================================* * cmp_string * *===========================================================================*/ PUBLIC int cmp_string(rsp1, rsp2, n) register char *rsp1, *rsp2; /* pointers to the two strings */ register int n; /* string length */ { /* Compare two strings of length 'n'. If they are the same, return 1. * If they differ, return 0. */ do { if (*rsp1++ != *rsp2++) return(0); } while (--n); /* The strings are identical. */ return(1); } /*===========================================================================* * copy * *===========================================================================*/ PUBLIC copy(dest, source, bytes) char *dest; /* destination pointer */ char *source; /* source pointer */ int bytes; /* how much data to move */ { /* Copy a byte string of length 'bytes' from 'source' to 'dest'. * If all three parameters are exactly divisible by the integer size, copy them * an integer at a time. Otherwise copy character-by-character. */ if (bytes <= 0) return; /* makes test-at-the-end possible */ if (bytes % sizeof(int) == 0 && (int) dest % sizeof(int) == 0 && (int) source % sizeof(int) == 0) { /* Copy the string an integer at a time. */ register int n = bytes/sizeof(int); register int *dpi = (int *) dest; register int *spi = (int *) source; do { *dpi++ = *spi++; } while (--n); } else { /* Copy the string character-by-character. */ register int n = bytes; register char *dpc = (char *) dest; register char *spc = (char *) source; do { *dpc++ = *spc++; } while (--n); } } /*===========================================================================* * fetch_name * *===========================================================================*/ PUBLIC int fetch_name(path, len, flag) char *path; /* pointer to the path in user space */ int len; /* path length, including 0 byte */ int flag; /* M3 means path may be in message */ { /* Go get path and put it in 'user_path'. * If 'flag' = M3 and 'len' <= M3_STRING, the path is present in 'message'. * If it is not, go copy it from user space. */ register char *rpu, *rpm; vir_bytes vpath; if (flag == M3 && len <= M3_STRING) { /* Just copy the path from the message to 'user_path'. */ rpu = &user_path[0]; rpm = pathname; /* contained in input message */ do { *rpu++ = *rpm++; } while (--len); return(OK); } /* String is not contained in the message. Go get it from user space. */ if (len > MAX_PATH) { err_code = E_LONG_STRING; return(ERROR); } vpath = (vir_bytes) path; err_code = rw_user(D, who, vpath, (vir_bytes) len, user_path, FROM_USER); return(err_code); } /*===========================================================================* * no_sys * *===========================================================================*/ PUBLIC int no_sys() { /* Somebody has used an illegal system call number */ return(EINVAL); } /*===========================================================================* * panic * *===========================================================================*/ PUBLIC panic(format, num) char *format; /* format string */ int num; /* number to go with format string */ { /* Something awful has happened. Panics are caused when an internal * inconsistency is detected, e.g., a programming error or illegal value of a * defined constant. */ if (panicking) return; /* do not panic during a sync */ panicking = TRUE; /* prevent another panic during the sync */ printf("File system panic: %s ", format); if (num != NO_NUM) printf("%d",num); printf("\n"); do_sync(); /* flush everything to the disk */ sys_abort(); } /* This file is the counterpart of "read.c". It contains the code for writing * insofar as this is not contained in read_write(). * * The entry points into this file are * do_write: call read_write to perform the WRITE system call * write_map: add a new zone to an inode * clear_zone: erase a zone in the middle of a file * new_block: acquire a new block */ #include "../h/const.h" #include "../h/type.h" #include "../h/error.h" #include "const.h" #include "type.h" #include "buf.h" #include "file.h" #include "fproc.h" #include "glo.h" #include "inode.h" #include "super.h" /*===========================================================================* * do_write * *===========================================================================*/ PUBLIC int do_write() { /* Perform the write(fd, buffer, nbytes) system call. */ return(read_write(WRITING)); } /*===========================================================================* * write_map * *===========================================================================*/ PRIVATE int write_map(rip, position, new_zone) register struct inode *rip; /* pointer to inode to be changed */ file_pos position; /* file address to be mapped */ zone_nr new_zone; /* zone # to be inserted */ { /* Write a new zone into an inode. */ int scale; zone_nr z, *zp; register block_nr b; long excess, zone; int index; struct buf *bp; int new_ind, new_dbl; extern zone_nr alloc_zone(); extern struct buf *get_block(); extern real_time clock_time(); rip->i_dirt = DIRTY; /* inode will be changed */ bp = NIL_BUF; scale = scale_factor(rip); /* for zone-block conversion */ zone = (position/BLOCK_SIZE) >> scale; /* relative zone # to insert */ /* Is 'position' to be found in the inode itself? */ if (zone < NR_DZONE_NUM) { rip->i_zone[zone] = new_zone; rip->i_modtime = clock_time(); return(OK); } /* It is not in the inode, so it must be single or double indirect. */ excess = zone - NR_DZONE_NUM; /* first NR_DZONE_NUM don't count */ new_ind = FALSE; new_dbl = FALSE; if (excess < NR_INDIRECTS) { /* 'position' can be located via the single indirect block. */ zp = &rip->i_zone[NR_DZONE_NUM]; } else { /* 'position' can be located via the double indirect block. */ if ( (z = rip->i_zone[NR_DZONE_NUM+1]) == NO_ZONE) { /* Create the double indirect block. */ if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE) return(err_code); rip->i_zone[NR_DZONE_NUM+1] = z; new_dbl = TRUE; /* set flag for later */ } /* Either way, 'z' is zone number for double indirect block. */ excess -= NR_INDIRECTS; /* single indirect doesn't count */ index = excess / NR_INDIRECTS; excess = excess % NR_INDIRECTS; if (index >= NR_INDIRECTS) return(EFBIG); b = (block_nr) z << scale; bp = get_block(rip->i_dev, b, (new_dbl ? NO_READ : NORMAL)); if (new_dbl) zero_block(bp); zp= &bp->b_ind[index]; } /* 'zp' now points to place where indirect zone # goes; 'excess' is index. */ if (*zp == NO_ZONE) { /* Create indirect block. */ *zp = alloc_zone(rip->i_dev, rip->i_zone[0]); new_ind = TRUE; if (bp != NIL_BUF) bp->b_dirt = DIRTY; /* if double ind, it is dirty */ if (*zp == NO_ZONE) { put_block(bp, INDIRECT_BLOCK); /* release dbl indirect blk */ return(err_code); /* couldn't create single ind */ } } put_block(bp, INDIRECT_BLOCK); /* release double indirect blk */ /* 'zp' now points to indirect block's zone number. */ b = (block_nr) *zp << scale; bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) ); if (new_ind) zero_block(bp); bp->b_ind[excess] = new_zone; rip->i_modtime = clock_time(); bp->b_dirt = DIRTY; put_block(bp, INDIRECT_BLOCK); return(OK); } /*===========================================================================* * clear_zone * *===========================================================================*/ PUBLIC clear_zone(rip, pos, flag) register struct inode *rip; /* inode to clear */ file_pos pos; /* points to block to clear */ int flag; /* 0 if called by read_write, 1 by new_block */ { /* Zero a zone, possibly starting in the middle. The parameter 'pos' gives * a byte in the first block to be zeroed. Clearzone() is called from * read_write and new_block(). */ register struct buf *bp; register block_nr b, blo, bhi; register file_pos next; register int scale; register zone_type zone_size; extern struct buf *get_block(); extern block_nr read_map(); /* If the block size and zone size are the same, clear_zone() not needed. */ if ( (scale = scale_factor(rip)) == 0) return; zone_size = (zone_type) BLOCK_SIZE << scale; if (flag == 1) pos = (pos/zone_size) * zone_size; next = pos + BLOCK_SIZE - 1; /* If 'pos' is in the last block of a zone, do not clear the zone. */ if (next/zone_size != pos/zone_size) return; if ( (blo = read_map(rip, next)) == NO_BLOCK) return; bhi = ( ((blo>>scale)+1) << scale) - 1; /* Clear all the blocks between 'blo' and 'bhi'. */ for (b = blo; b <= bhi; b++) { bp = get_block(rip->i_dev, b, NO_READ); zero_block(bp); put_block(bp, FULL_DATA_BLOCK); } } /*===========================================================================* * new_block * *===========================================================================*/ PUBLIC struct buf *new_block(rip, position) register struct inode *rip; /* pointer to inode */ file_pos position; /* file pointer */ { /* Acquire a new block and return a pointer to it. Doing so may require * allocating a complete zone, and then returning the initial block. * On the other hand, the current zone may still have some unused blocks. */ register struct buf *bp; block_nr b, base_block; zone_nr z; zone_type zone_size; int scale, r; struct super_block *sp; extern struct buf *get_block(); extern struct super_block *get_super(); extern block_nr read_map(); extern zone_nr alloc_zone(); /* Is another block available in the current zone? */ if ( (b = read_map(rip, position)) == NO_BLOCK) { /* Choose first zone if need be. */ if (rip->i_size == 0) { sp = get_super(rip->i_dev); z = sp->s_firstdatazone; } else { z = rip->i_zone[0]; } if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NIL_BUF); if ( (r = write_map(rip, position, z)) != OK) { free_zone(rip->i_dev, z); err_code = r; return(NIL_BUF); } /* If we are not writing at EOF, clear the zone, just to be safe. */ if ( position != rip->i_size) clear_zone(rip, position, 1); scale = scale_factor(rip); base_block = (block_nr) z << scale; zone_size = (zone_type) BLOCK_SIZE << scale; b = base_block + (block_nr)((position % zone_size)/BLOCK_SIZE); } bp = get_block(rip->i_dev, b, NO_READ); zero_block(bp); return(bp); } /*===========================================================================* * zero_block * *===========================================================================*/ PUBLIC zero_block(bp) register struct buf *bp; /* pointer to buffer to zero */ { /* Zero a block. */ register int n; register int *zip; n = INTS_PER_BLOCK; /* number of integers in a block */ zip = bp->b_int; /* where to start clearing */ do { *zip++ = 0;} while (--n); bp->b_dirt = DIRTY; } Н!..."makefileCFLAGS= -Di8088 -w -F -T. h=../h l=/usr/lib obj = main.s open.s read.s write.s pipe.s device.s \ path.s mount.s link.s super.s inode.s cache.s filedes.s \ stadir.s protect.s time.s misc.s utility.s table.s putc.s fs: makefile $l/head.s $(obj) $l/libc.a $l/end.s @echo "Start linking FS. " @echo "If RAM disk fills up, edit makefile to use asld -T. " @echo "On a PC, /lib/* will be removed to make space on RAM disk" @rm -f /lib/cem /lib/cpp /tmp/* asld -o fs $l/head.s $(obj) $l/libc.a $l/end.s @echo "FS done. On a PC, please restore /lib/cem and /lib/cpp manually" cache.s: const.h type.h $h/const.h $h/type.h cache.s: $h/error.h cache.s: buf.h cache.s: file.h cache.s: fproc.h cache.s: glo.h cache.s: inode.h cache.s: super.h device.s: const.h type.h $h/const.h $h/type.h device.s: $h/com.h device.s: $h/error.h device.s: dev.h device.s: file.h device.s: fproc.h device.s: glo.h device.s: inode.h device.s: param.h filedes.s: const.h type.h $h/const.h $h/type.h filedes.s: $h/error.h filedes.s: file.h filedes.s: fproc.h filedes.s: glo.h filedes.s: inode.h inode.s: const.h type.h $h/const.h $h/type.h inode.s: $h/error.h inode.s: buf.h inode.s: file.h inode.s: fproc.h inode.s: glo.h inode.s: inode.h inode.s: super.h link.s: const.h type.h $h/const.h $h/type.h link.s: $h/error.h link.s: buf.h link.s: file.h link.s: fproc.h link.s: glo.h link.s: inode.h link.s: param.h main.s: const.h type.h $h/const.h $h/type.h main.s: $h/callnr.h main.s: $h/com.h main.s: $h/error.h main.s: buf.h main.s: file.h main.s: fproc.h main.s: glo.h main.s: inode.h main.s: param.h main.s: super.h misc.s: const.h type.h $h/const.h $h/type.h misc.s: $h/callnr.h misc.s: $h/com.h misc.s: $h/error.h misc.s: buf.h misc.s: file.h misc.s: fproc.h misc.s: glo.h misc.s: inode.h misc.s: param.h misc.s: super.h mount.s: const.h type.h $h/const.h $h/type.h mount.s: $h/error.h mount.s: buf.h mount.s: file.h mount.s: fproc.h mount.s: glo.h mount.s: inode.h mount.s: param.h mount.s: super.h open.s: const.h type.h $h/const.h $h/type.h open.s: $h/callnr.h open.s: $h/error.h open.s: buf.h open.s: file.h open.s: fproc.h open.s: glo.h open.s: inode.h open.s: param.h path.s: const.h type.h $h/const.h $h/type.h path.s: $h/error.h path.s: buf.h path.s: file.h path.s: fproc.h path.s: glo.h path.s: inode.h path.s: super.h pipe.s: const.h type.h $h/const.h $h/type.h pipe.s: $h/callnr.h pipe.s: $h/com.h pipe.s: $h/error.h pipe.s: $h/signal.h pipe.s: file.h pipe.s: fproc.h pipe.s: glo.h pipe.s: inode.h pipe.s: param.h protect.s: const.h type.h $h/const.h $h/type.h protect.s: $h/error.h protect.s: buf.h protect.s: file.h protect.s: fproc.h protect.s: glo.h protect.s: inode.h protect.s: param.h protect.s: super.h putc.s: const.h type.h $h/const.h $h/type.h putc.s: $h/com.h read.s: const.h type.h $h/const.h $h/type.h read.s: $h/com.h read.s: $h/error.h read.s: buf.h read.s: file.h read.s: fproc.h read.s: glo.h read.s: inode.h read.s: param.h read.s: super.h stadir.s: const.h type.h $h/const.h $h/type.h stadir.s: $h/error.h stadir.s: $h/stat.h stadir.s: file.h stadir.s: fproc.h stadir.s: glo.h stadir.s: inode.h stadir.s: param.h super.s: const.h type.h $h/const.h $h/type.h super.s: $h/error.h super.s: buf.h super.s: inode.h super.s: super.h table.s: const.h type.h $h/const.h $h/type.h table.s: $h/com.h table.s: $h/callnr.h table.s: $h/error.h table.s: $h/stat.h table.s: buf.h table.s: dev.h table.s: file.h table.s: fproc.h table.s: glo.h table.s: inode.h table.s: super.h time.s: const.h type.h $h/const.h $h/type.h time.s: $h/callnr.h time.s: $h/com.h time.s: $h/error.h time.s: file.h time.s: fproc.h time.s: glo.h time.s: inode.h time.s: param.h utility.s: const.h type.h $h/const.h $h/type.h utility.s: $h/com.h utility.s: $h/error.h utility.s: buf.h utility.s: file.h utility.s: fproc.h utility.s: glo.h utility.s: inode.h utility.s: param.h utility.s: super.h write.s: const.h type.h $h/const.h $h/type.h write.s: $h/error.h write.s: buf.h write.s: file.h write.s: fproc.h write.s: glo.h write.s: inode.h write.s: super.h #...$makefileCFLAGS = -Di8088 -O h=../h l=../lib obj = main.o open.o read.o write.o pipe.o device.o \ path.o mount.o link.o super.o inode.o cache.o filedes.o \ stadir.o protect.o time.o misc.o utility.o table.o putc.o fs: makefile $l/head.o $(obj) $l/libc.a $l/end.o @echo "Start linking FS" @ld -o fs $l/head.o $(obj) $l/libc.a $l/end.o @echo "FS done" cache.o: const.h type.h $h/const.h $h/type.h cache.o: $h/error.h cache.o: buf.h cache.o: file.h cache.o: fproc.h cache.o: glo.h cache.o: inode.h cache.o: super.h device.o: const.h type.h $h/const.h $h/type.h device.o: $h/com.h device.o: $h/error.h device.o: dev.h device.o: file.h device.o: fproc.h device.o: glo.h device.o: inode.h device.o: param.h filedes.o: const.h type.h $h/const.h $h/type.h filedes.o: $h/error.h filedes.o: file.h filedes.o: fproc.h filedes.o: glo.h filedes.o: inode.h inode.o: const.h type.h $h/const.h $h/type.h inode.o: $h/error.h inode.o: buf.h inode.o: file.h inode.o: fproc.h inode.o: glo.h inode.o: inode.h inode.o: super.h link.o: const.h type.h $h/const.h $h/type.h link.o: $h/error.h link.o: buf.h link.o: file.h link.o: fproc.h link.o: glo.h link.o: inode.h link.o: param.h main.o: const.h type.h $h/const.h $h/type.h main.o: $h/callnr.h main.o: $h/com.h main.o: $h/error.h main.o: buf.h main.o: file.h main.o: fproc.h main.o: glo.h main.o: inode.h main.o: param.h main.o: super.h misc.o: const.h type.h $h/const.h $h/type.h misc.o: $h/callnr.h misc.o: $h/com.h misc.o: $h/error.h misc.o: buf.h misc.o: file.h misc.o: fproc.h misc.o: glo.h misc.o: inode.h misc.o: param.h misc.o: super.h mount.o: const.h type.h $h/const.h $h/type.h mount.o: $h/error.h mount.o: buf.h mount.o: file.h mount.o: fproc.h mount.o: glo.h mount.o: inode.h mount.o: param.h mount.o: super.h open.o: const.h type.h $h/const.h $h/type.h open.o: $h/callnr.h open.o: $h/error.h open.o: buf.h open.o: file.h open.o: fproc.h open.o: glo.h open.o: inode.h open.o: param.h path.o: const.h type.h $h/const.h $h/type.h path.o: $h/error.h path.o: buf.h path.o: file.h path.o: fproc.h path.o: glo.h path.o: inode.h path.o: super.h pipe.o: const.h type.h $h/const.h $h/type.h pipe.o: $h/callnr.h pipe.o: $h/com.h pipe.o: $h/error.h pipe.o: $h/signal.h pipe.o: file.h pipe.o: fproc.h pipe.o: glo.h pipe.o: inode.h pipe.o: param.h protect.o: const.h type.h $h/const.h $h/type.h protect.o: $h/error.h protect.o: buf.h protect.o: file.h protect.o: fproc.h protect.o: glo.h protect.o: inode.h protect.o: param.h protect.o: super.h putc.o: const.h type.h $h/const.h $h/type.h putc.o: $h/com.h read.o: const.h type.h $h/const.h $h/type.h read.o: $h/com.h read.o: $h/error.h read.o: buf.h read.o: file.h read.o: fproc.h read.o: glo.h read.o: inode.h read.o: param.h read.o: super.h stadir.o: const.h type.h $h/const.h $h/type.h stadir.o: $h/error.h stadir.o: $h/stat.h stadir.o: file.h stadir.o: fproc.h stadir.o: glo.h stadir.o: inode.h stadir.o: param.h super.o: const.h type.h $h/const.h $h/type.h super.o: $h/error.h super.o: buf.h super.o: inode.h super.o: super.h table.o: const.h type.h $h/const.h $h/type.h table.o: $h/com.h table.o: $h/callnr.h table.o: $h/error.h table.o: $h/stat.h table.o: buf.h table.o: dev.h table.o: file.h table.o: fproc.h table.o: glo.h table.o: inode.h table.o: super.h time.o: const.h type.h $h/const.h $h/type.h time.o: $h/callnr.h time.o: $h/com.h time.o: $h/error.h time.o: file.h time.o: fproc.h time.o: glo.h time.o: inode.h time.o: param.h utility.o: const.h type.h $h/const.h $h/type.h utility.o: $h/com.h utility.o: $h/error.h utility.o: buf.h utility.o: file.h utility.o: fproc.h utility.o: glo.h utility.o: inode.h utility.o: param.h utility.o: super.h write.o: const.h type.h $h/const.h $h/type.h write.o: $h/error.h write.o: buf.h write.o: file.h write.o: fproc.h write.o: glo.h write.o: inode.h write.o: super.h %...&make.bat'_link.bat(linklistecho off if "%1"=="-l" _link.bat echo/ echo Making File system task echo/ echo To re-make the file system task after you changed echo a module, just delete the .OBJ file for that module. echo Specifying a '-l' switch will only do the linking echo (e.g. after a manual compilation of a module). echo/ cd ..\lib if exist head.OBJ goto cc echo assembling head masm head,,nul,nul >..\fs\head.lst :cc cd ..\fs if exist write.OBJ goto fout echo compiling write cc1 -di8088 write >write.lst if errorlevel 1 goto fout cc2 write >>write.lst if errorlevel 1 goto fout cc3 write>>write.lst if errorlevel 1 goto fout cc4 write>>write.lst :fout rem if exist cache.OBJ goto fout2 echo compiling cache cc1 -di8088 cache>cache.lst if errorlevel 1 goto fout2 cc2 cache>>cache.lst if errorlevel 1 goto fout2 cc3 cache>>cache.lst if errorlevel 1 goto fout2 cc4 cache>>cache.lst :fout2 rem if exist device.OBJ goto fout3 echo compiling devices cc1 -di8088 device>device.lst if errorlevel 1 goto fout3 cc2 device >>device.lst if errorlevel 1 goto fout3 cc3 device >>device.lst if errorlevel 1 goto fout3 cc4 device >>device.lst :fout3 rem if exist filedes.OBJ goto fout4 echo compiling filedes cc1 -di8088 filedes >filedes.lst if errorlevel 1 goto fout4 cc2 filedes >>filedes.lst if errorlevel 1 goto fout4 cc3 filedes >>filedes.lst if errorlevel 1 goto fout4 cc4 filedes >>filedes.lst :fout4 rem if exist inode.OBJ goto fout5 echo compiling inode cc1 -di8088 inode >inode.lst if errorlevel 1 goto fout5 cc2 inode >>inode.lst if errorlevel 1 goto fout5 cc3 inode >>inode.lst if errorlevel 1 goto fout5 cc4 inode >>inode.lst :fout5 rem if exist main.OBJ goto fout6 echo compiling main cc1 -di8088 main >>main.lst if errorlevel 1 goto fout6 cc2 main >>main.lst if errorlevel 1 goto fout6 cc3 main >>main.lst if errorlevel 1 goto fout6 cc4 main >>main.lst :fout6 rem if exist link.OBJ goto fout7 echo compiling link cc1 -di8088 link >link.lst if errorlevel 1 goto fout7 cc2 link >>link.lst if errorlevel 1 goto fout7 cc3 link >>link.lst if errorlevel 1 goto fout7 cc4 link >>link.lst :fout7 rem if exist misc.OBJ goto fout8 echo compiling misc cc1 -di8088 misc >misc.lst if errorlevel 1 goto fout8 cc2 misc >>misc.lst if errorlevel 1 goto fout8 cc3 misc >>misc.lst if errorlevel 1 goto fout8 cc4 misc >>misc.lst :fout8 rem if exist mount.OBJ goto fout9 echo compiling mount cc1 -di8088 mount >mount.lst if errorlevel 1 goto fout9 cc2 mount >>mount.lst if errorlevel 1 goto fout9 cc3 mount >>mount.lst if errorlevel 1 goto fout9 cc4 mount >>mount.lst :fout9 rem if exist open.OBJ goto fout10 echo compiling open cc1 -di8088 open >open.lst if errorlevel 1 goto fout10 cc2 open >>open.lst if errorlevel 1 goto fout10 cc3 open >>open.lst if errorlevel 1 goto fout10 cc4 open >>open.lst :fout10 if exist path.OBJ goto fout11 echo compiling path cc1 -di8088 path >path.lst if errorlevel 1 goto fout11 cc2 path >>path.lst if errorlevel 1 goto fout11 cc3 path >>path.lst if errorlevel 1 goto fout11 cc4 path >>path.lst :fout11 if exist pipe.OBJ goto fout12 echo compiling pipe cc1 -di8088 pipe >pipe.lst if errorlevel 1 goto fout12 cc2 pipe >>pipe.lst if errorlevel 1 goto fout12 cc3 pipe >>pipe.lst if errorlevel 1 goto fout12 cc4 pipe >>pipe.lst :fout12 if exist protect.OBJ goto fout13 echo compiling protect cc1 -di8088 protect >protect.lst if errorlevel 1 goto fout13 cc2 protect >>protect.lst if errorlevel 1 goto fout13 cc3 protect >>protect.lst if errorlevel 1 goto fout13 cc4 protect >>protect.lst :fout13 if exist putc.OBJ goto fout14 echo compiling putc cc1 -di8088 putc >putc.lst if errorlevel 1 goto fout14 cc2 putc >>putc.lst if errorlevel 1 goto fout14 cc3 putc >>putc.lst if errorlevel 1 goto fout14 cc4 putc >>putc.lst :fout14 if exist read.OBJ goto fout15 echo compiling read cc1 -di8088 read >read.lst if errorlevel 1 goto fout15 cc2 read >>read.lst if errorlevel 1 goto fout15 cc3 read >>read.lst if errorlevel 1 goto fout15 cc4 read >>read.lst :fout15 if exist stadir.OBJ goto fout16 echo compiling stadir cc1 -di8088 stadir >stadir.lst if errorlevel 1 goto fout16 cc2 stadir >>stadir.lst if errorlevel 1 goto fout16 cc3 stadir >>stadir.lst if errorlevel 1 goto fout16 cc4 stadir >>stadir.lst :fout16 if exist super.OBJ goto fout17 echo compiling super cc1 -di8088 super >super.lst if errorlevel 1 goto fout17 cc2 super >>super.lst if errorlevel 1 goto fout17 cc3 super >>super.lst if errorlevel 1 goto fout17 cc4 super >>super.lst :fout17 if exist table.OBJ goto fout18 echo compiling table cc1 -di8088 table >table.lst if errorlevel 1 goto fout18 cc2 table >>table.lst if errorlevel 1 goto fout18 cc3 table >>table.lst if errorlevel 1 goto fout18 cc4 table >>table.lst :fout18 if exist time.OBJ goto fout19 echo compiling time cc1 -di8088 time >time.lst if errorlevel 1 goto fout19 cc2 time >>time.lst if errorlevel 1 goto fout19 cc3 time >>time.lst if errorlevel 1 goto fout19 cc4 time >>time.lst :fout19 if exist utility.OBJ goto fout20 echo compiling utility cc1 -di8088 utility >utility.lst if errorlevel 1 goto fout20 cc2 utility >>utility.lst if errorlevel 1 goto fout20 cc3 utility >>utility.lst if errorlevel 1 goto fout20 cc4 utility >>utility.lst :fout20 echo/ _link.bat echo/ echo Linking file system task link/m ..\lib\head+ fs.lst dos2out -d fs >>fs.lst echo/ echo FS done. Check the .lst-files for errors. pause echo on for %%f in (*.lst) do type %%f main+open+read+write+pipe+device+path+ mount+link+super+inode+cache+filedes+stadir+ protect+time+misc+utility+table+putc fs fs ..\lib\mxc86 )...*READ_ME+run,libsrc.a-MINIX2PCIX7C86The sources to the library are contained in an archive to save disk space. Since MINIX uses a minimum of 1K for each file, no matter how small, the sources take up less space when packed together in an archive. To extract them, make sure there is at least 130K of free disk space, and type: ar xv libsrc.a Furthermore, please note that not all the library routines are contained in /usr/lib/libc.a in order to save disk space. If you have a hard disk, compare the contents of libsrc.a with libc.a, and compile the missing routines with cc -c -LIB file.c. Add these to libc.a. With a floppy disk system, you may not have room for the missing ones. # ========== compile everything =========== for i in *.c do echo $i cc -c -LIB $i done # ========== build library ============== # # Build libc.a using all of the .s files in the current directory. # New files are placed at the beginning of the library followed by # modules which are already in the old library (/usr/lib/libc.a). # Old modules are placed in the new library in the same order in which # they appear in the old library. # # Art Zemon, July 17, 1987 # echo Reading old library ar t /usr/lib/libc.a > list split -8 list cat /dev/null > orig.order echo Creating orig.order for f in x?? do echo ar a libc.a `cat $f` >> orig.order rm $f done # # Figure out which files are not in the existing library. # Base this decision on the .c files because there are a couple of .s # files here which don't belong in the library. # echo Checking .c files cat /dev/null > new.files for c in *.c do f=`basename $c .c`.s echo $f if grep -s $f list > /dev/null 2>&1 ; then cat /dev/null ; else echo ar av libc.a $f >> new.files ; fi done # # Figure out which modules are in the library but not here and extract them # from the library. # echo determining which modules must be extracted from old library rm -f libc.a sh orig.order > /dev/null 2> missing ar xv /usr/lib/libc.a `grep 'Cannot find ' missing | gres 'Cannot find ' ''` rm missing libc.a list # # Construct the library # echo constructing new library with new modules sh new.files echo appending original modules to new library sh orig.order echo done -- -- Art Zemon FileNet Corporation Costa Mesa, California ...!hplabs!felix!zemon eџabort.cС [Я Єabort() { exit(99); } abs.cС [Я Є#abs(i){ return i < 0 ? -i : i; } access.cС [Я Є#include "../include/lib.h" PUBLIC int access(name, mode) char *name; int mode; { return callm3(FS, ACCESS, mode, name); } alarm.cС [Я Є#include "../include/lib.h" PUBLIC int alarm(sec) unsigned sec; { return callm1(MM, ALARM, (int) sec, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } atoi.cС [Я Є#define isascii(c) (((unsigned) (c) & 0xFF) < 0200) /* For all the following functions the parameter must be ASCII */ #define _between(a,b,c) ((unsigned) ((b) - (a)) < (c) - (a)) #define isupper(c) _between('A', c, 'Z') #define islower(c) _between('a', c, 'z') #define isdigit(c) _between('0', c, '9') #define isprint(c) _between(' ', c, '~') /* The others are problematic as the parameter may only be evaluated once */ static _c_; /* used to store the evaluated parameter */ #define isalpha(c) (isupper(_c_ = (c)) || islower(_c_)) #define isalnum(c) (isalpha(c) || isdigit(_c_)) #define _isblank(c) ((_c_ = (c)) == ' ' || _c_ == '\t') #define isspace(c) (_isblank(c) || _c_=='\r' || _c_=='\n' || _c_=='\f') #define iscntrl(c) ((_c_ = (c)) == 0177 || _c_ < ' ') atoi(s) register char *s; { register int total = 0; register unsigned digit; register minus = 0; while (isspace(*s)) s++; if (*s == '-') { s++; minus = 1; } while ((digit = *s++ - '0') < 10) { total *= 10; total += digit; } return(minus ? -total : total); } atol.cС [Я Є?#include "../include/ctype.h" long atol(s) register char *s; { register long total = 0; register unsigned digit; register minus = 0; while (isspace(*s)) s++; if (*s == '-') { s++; minus = 1; } while ((digit = *s++ - '0') < 10) { total *= 10; total += digit; } return(minus ? -total : total); } tbcopy.cС [Я Єtbcopy(old, new, n) register char *old, *new; int n; { /* Copy a block of data. */ while (n--) *new++ = *old++; } brk.cС [Я Єм#include "../include/lib.h" extern char *brksize; PUBLIC char *brk(addr) char *addr; { int k; k = callm1(MM, BRK, 0, 0, 0, addr, NIL_PTR, NIL_PTR); if (k == OK) { brksize = M.m2_p1; return(NIL_PTR); } else { return( (char*) -1 ); } } PUBLIC char *sbrk(incr) char *incr; { char *newsize, *oldsize; extern int endv, dorgv; oldsize = brksize; newsize = brksize + (int) incr; if (brk(newsize) == 0) return(oldsize); else return( (char *) -1 ); } brk2.cС [Я Є‹#include "../include/lib.h" PUBLIC brk2() { char *p1, *p2; p1 = (char *) get_size(); callm1(MM, BRK2, 0, 0, 0, p1, p2, NIL_PTR); } _call.cС [Я Є'#include "../include/lib.h" PUBLIC int errno; /* place where error numbers go */ PUBLIC int callm1(proc, syscallnr, int1, int2, int3, ptr1, ptr2, ptr3) int proc; /* FS or MM */ int syscallnr; /* which system call */ int int1; /* first integer parameter */ int int2; /* second integer parameter */ int int3; /* third integer parameter */ char *ptr1; /* pointer parameter */ char *ptr2; /* pointer parameter */ char *ptr3; /* pointer parameter */ { /* Send a message and get the response. The 'M.m_type' field of the * reply contains a value (>= 0) or an error code (<0). Use message format m1. */ M.m1_i1 = int1; M.m1_i2 = int2; M.m1_i3 = int3; M.m1_p1 = ptr1; M.m1_p2 = ptr2; M.m1_p3 = ptr3; return callx(proc, syscallnr); } PUBLIC int callm3(proc, syscallnr, int1, name) int proc; /* FS or MM */ int syscallnr; /* which system call */ int int1; /* integer parameter */ char *name; /* string */ { /* This form of system call is used for those calls that contain at most * one integer parameter along with a string. If the string fits in the * message, it is copied there. If not, a pointer to it is passed. */ register int k; register char *rp; k = len(name); M.m3_i1 = k; M.m3_i2 = int1; M.m3_p1 = name; rp = &M.m3_ca1[0]; if (k <= M3_STRING) while (k--) *rp++ = *name++; return callx(proc, syscallnr); } PUBLIC int callx(proc, syscallnr) int proc; /* FS or MM */ int syscallnr; /* which system call */ { /* Send a message and get the response. The 'M.m_type' field of the * reply contains a value (>= 0) or an error code (<0). */ int k; M.m_type = syscallnr; k = sendrec(proc, &M); if (k != OK) return(k); /* send itself failed */ if (M.m_type < 0) {errno = -M.m_type; return(-1);} return(M.m_type); } PUBLIC int len(s) register char *s; /* character string whose length is needed */ { /* Return the length of a character string, including the 0 at the end. */ register int k; k = 0; while (*s++ != 0) k++; return(k+1); /* return length including the 0-byte at end */ } chdir.cС \Я Єj#include "../include/lib.h" PUBLIC int chdir(name) char *name; { return callm3(FS, CHDIR, 0, name); } chmod.cС \Я Є}#include "../include/lib.h" PUBLIC int chmod(name, mode) char* name; int mode; { return callm3(FS, CHMOD, mode, name); } rchown.cС \Я ЄЋ#include "../include/lib.h" PUBLIC int chown(name, owner, grp) char *name; int owner, grp; { return callm1(FS, CHOWN, len(name), owner, grp, name, NIL_PTR, NIL_PTR); } )chroot.cС \Я Єl#include "../include/lib.h" PUBLIC int chroot(name) char* name; { return callm3(FS, CHROOT, 0, name); } cleanup.cС \Я Є™#include "../include/stdio.h" _cleanup() { register int i; for ( i = 0 ; i < NFILES ; i++ ) if ( _io_table[i] != NULL ) fflush(_io_table[i]); } Sclose.cС \Я Є€#include "../include/lib.h" PUBLIC int close(fd) int fd; { return callm1(FS, CLOSE, fd, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } creat.cС \Я Є|#include "../include/lib.h" PUBLIC int creat(name, mode) char* name; int mode; { return callm3(FS, CREAT, mode, name); } crypt.cС \Я Є/* From Andy Tanenbaum's book "Computer Networks", rewritten in C */ struct block { unsigned char b_data[64]; }; struct ordering { unsigned char o_data[64]; }; static struct block key; static struct ordering InitialTr = { 58,50,42,34,26,18,10, 2,60,52,44,36,28,20,12, 4, 62,54,46,38,30,22,14, 6,64,56,48,40,32,24,16, 8, 57,49,41,33,25,17, 9, 1,59,51,43,35,27,19,11, 3, 61,53,45,37,29,21,13, 5,63,55,47,39,31,23,15, 7, }; static struct ordering FinalTr = { 40, 8,48,16,56,24,64,32,39, 7,47,15,55,23,63,31, 38, 6,46,14,54,22,62,30,37, 5,45,13,53,21,61,29, 36, 4,44,12,52,20,60,28,35, 3,43,11,51,19,59,27, 34, 2,42,10,50,18,58,26,33, 1,41, 9,49,17,57,25, }; static struct ordering swap = { 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, }; static struct ordering KeyTr1 = { 57,49,41,33,25,17, 9, 1,58,50,42,34,26,18, 10, 2,59,51,43,35,27,19,11, 3,60,52,44,36, 63,55,47,39,31,23,15, 7,62,54,46,38,30,22, 14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4, }; static struct ordering KeyTr2 = { 14,17,11,24, 1, 5, 3,28,15, 6,21,10, 23,19,12, 4,26, 8,16, 7,27,20,13, 2, 41,52,31,37,47,55,30,40,51,45,33,48, 44,49,39,56,34,53,46,42,50,36,29,32, }; static struct ordering etr = { 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9,10,11,12,13,12,13,14,15,16,17, 16,17,18,19,20,21,20,21,22,23,24,25, 24,25,26,27,28,29,28,29,30,31,32, 1, }; static struct ordering ptr = { 16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10, 2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25, }; static unsigned char s_boxes[8][64] = { { 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, }, { 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, }, { 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,1орстуфхцчшщъыьэюя№ёђѓєѕіїјљњћќ§ўџ      !"#$%&'()*+,-./0123456789:;<=2, }, { 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, }, { 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, }, { 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, }, { 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, }, { 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, }, }; static int rots[] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1, }; static transpose(data, t, n) register struct block *data; register struct ordering *t; register int n; { struct block x; x = *data; while (n-- > 0) { data->b_data[n] = x.b_data[t->o_data[n] - 1]; } } static rotate(key) register struct block *key; { register unsigned char *p = key->b_data; register unsigned char *ep = &(key->b_data[55]); int data0 = key->b_data[0], data28 = key->b_data[28]; while (p++ < ep) *(p-1) = *p; key->b_data[27] = data0; key->b_data[55] = data28; } static struct ordering *EP = &etr; static f(i, key, a, x) struct block *key, *a; register struct block *x; { struct block e, ikey, y; int k; register unsigned char *p, *q, *r; e = *a; transpose(&e, EP, 48); for (k = rots[i]; k; k--) rotate(key); ikey = *key; transpose(&ikey, &KeyTr2, 48); p = &(y.b_data[48]); q = &(e.b_data[48]); r = &(ikey.b_data[48]); while (p > y.b_data) { *--p = *--q ^ *--r; } q = x->b_data; for (k = 0; k < 8; k++) { register int xb, r; r = *p++ << 5; r += *p++ << 3; r += *p++ << 2; r += *p++ << 1; r += *p++; r += *p++ << 4; xb = s_boxes[k][r]; *q++ = (xb >> 3) & 1; *q++ = (xb>>2) & 1; *q++ = (xb>>1) & 1; *q++ = (xb & 1); } transpose(x, &ptr, 32); } setkey(k) register char *k; { key = *((struct block *) k); transpose(&key, &KeyTr1, 56); } encrypt(blck, edflag) char *blck; { register struct block *p = (struct block *) blck; register int i; transpose(p, &InitialTr, 64); for (i = 15; i>= 0; i--) { int j = edflag ? i : 15 - i; register int k; struct block b, x; b = *p; for (k = 31; k >= 0; k--) { p->b_data[k] = b.b_data[k + 32]; } f(j, &key, p, &x); for (k = 31; k >= 0; k--) { p->b_data[k+32] = b.b_data[k] ^ x.b_data[k]; } } transpose(p, &swap, 64); transpose(p, &FinalTr, 64); } char * crypt(pw,salt) register char *pw; char *salt; { /* Unfortunately, I had to look at the sources of V7 crypt. There was no other way to find out what this routine actually does. */ char pwb[66]; static char result[16]; register char *p = pwb; struct ordering new_etr; register int i; while (*pw && p < &pwb[64]) { register int j = 7; while (j--) { *p++ = (*pw >> j) & 01; } pw++; *p++ = 0; } while (p < &pwb[64]) *p++ = 0; setkey(p = pwb); while (p < &pwb[66]) *p++ = 0; new_etr = etr; EP = &new_etr; for (i = 0; i < 2; i++) { register char c = *salt++; register int j; result[i] = c; if ( c > 'Z') c -= 6 + 7 + '.'; /* c was a lower case letter */ else if ( c > '9') c -= 7 + '.';/* c was upper case letter */ else c -= '.'; /* c was digit, '.' or '/'. */ /* now, 0 <= c <= 63 */ for (j = 0; j < 6; j++) { if ((c >> j) & 01) { int t = 6*i + j; int temp = new_etr.o_data[t]; new_etr.o_data[t] = new_etr.o_data[t+24]; new_etr.o_data[t+24] = temp; } } } if (result[1] == 0) result[1] = result[0]; for (i = 0; i < 25; i++) encrypt(pwb,0); EP = &etr; p = pwb; pw = result+2; while (p < &pwb[66]) { register int c = 0; register int j = 6; while (j--) { c <<= 1; c |= *p++; } c += '.'; /* becomes >= '.' */ if (c > '9') c += 7; /* not in [./0-9], becomes upper */ if (c > 'Z') c += 6; /* not in [A-Z], becomes lower */ *pw++ = c; } *pw = 0; return result; } ctype.cС \Я Єl#include "../include/ctype.h" char _ctype_[] = { 0, _C, _C, _C, _C, _C, _C, _C, _C, _C, _S, _S, _S, _S, _S, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _S, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _P, _N, _N, _N, _N, _N, _N, _N, _N, _N, _N, _P, _P, _P, _P, _P, _P, _P, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _U, _P, _P, _P, _P, _P, _P, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _L, _P, _P, _P, _P, _C }; doprintf.cС \Я ЄЯ #include "../include/stdio.h" #define MAXDIGITS 12 #define PRIVATE static /* This is the same as varargs , on BSD systems */ #define GET_ARG(arglist,mode) ((mode *)(arglist += sizeof(mode)))[-1] _doprintf(fp, format, args) FILE *fp; register char *format; int args; { register char *vl; int r, w1, w2, sign; long l; char c; char *s; char padchar; char a[MAXDIGITS]; vl = (char *) args; while ( *format != '\0') { if ( *format != '%' ) { putc( *format++, fp ); continue; } w1 = 0; w2 = 0; sign = 1; padchar = ' '; format++; if ( *format == '-' ) { sign = -1; format++; } if ( *format == '0') { padchar = '0'; format++; } while ( *format >='0' && *format <='9' ){ w1 = 10 * w1 + sign * ( *format - '0' ); format++; } if ( *format == '.'){ format++; while ( *format >='0' && *format <='9' ){ w2 = 10 * w2 + ( *format - '0' ); format++; } } switch (*format) { case 'd': l = (long) GET_ARG(vl, int); r = 10; break; case 'u': l = (long) GET_ARG(vl, int); l = l & 0xFFFF; r = 10; break; case 'o': l = (long) GET_ARG(vl, int); if (l < 0) l = l & 0xFFFF; r = 8; break; case 'x': l = (long) GET_ARG(vl, int); if (l < 0) l = l & 0xFFFF; r = 16; break; case 'D': l = (long) GET_ARG(vl, long); r = 10; break; case 'O': l = (long) GET_ARG(vl, long); r = 8; break; case 'X': l = (long) GET_ARG(vl, long); r = 16; break; case 'c': c = (char) GET_ARG(vl, int); /* char's are casted back to int's */ putc( c, fp); format++; continue; case 's': s = GET_ARG(vl, char *); _printit(s,w1,w2,padchar,strlen(s),fp); format++; continue; default: putc('%',fp); putc(*format++,fp); continue; } _bintoascii (l, r, a); _printit(a,w1,w2,padchar,strlen(a),fp); format++; } } PRIVATE _bintoascii (num, radix, a) long num; int radix; char *a; { char b[MAXDIGITS]; int hit, negative; register int n, i; negative = 0; if (num == 0) { a[0] = '0'; a[1] = '\0'; return; } if (radix == 10) { if (num < 0) {num = -num; negative++;} } for (n = 0; n < MAXDIGITS; n++) b[n] = 0; n = 0; do { if (radix == 10) { b[n] = num % 10; num = (num - b[n]) / 10; } if (radix == 8) { b[n] = num & 0x7; num = (num >> 3) & 0x1FFFFFFF; } if (radix == 16) { b[n] = num & 0xF; num = (num >> 4) & 0x0FFFFFFF; } n++; } while (num != 0); /* Convert to ASCII. */ hit = 0; for (i = n - 1; i >= 0; i--) { if (b[i] == 0 && hit == 0) { b[i] = ' '; } else { if (b[i] < 10) b[i] += '0'; else b[i] += 'A' - 10; hit++; } } if (negative) b[n++] = '-'; for(i = n - 1 ; i >= 0 ; i-- ) *a++ = b[i]; *a = '\0'; } PRIVATE _printit(str, w1, w2, padchar, length, file) char *str; int w1, w2; char padchar; int length; FILE *file; { int len2 = length; int temp; if ( w2 > 0 && length > w2) len2 = w2; temp = len2; if ( w1 > 0 ) while ( w1 > len2 ){ --w1; putc(padchar,file); } while ( *str && ( len2-- != 0 )) putc(*str++, file); if ( w1 < 0 ) if ( padchar == '0' ){ putc('.',file); w1++; } while ( w1 < -temp ){ w1++; putc(padchar,file); } } dup.cС \Я Є{#include "../include/lib.h" PUBLIC int dup(fd) int fd; { return callm1(FS, DUP, fd, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } 'dup2.cС ]Я Є#include "../include/lib.h" PUBLIC int dup2(fd, fd2) int fd, fd2; { return callm1(FS, DUP, fd+0100, fd2, 0, NIL_PTR, NIL_PTR, NIL_PTR); } exec.cС ]Я Єћ#include "../include/lib.h" char *nullptr[1]; /* the EXEC calls need a zero pointer */ PUBLIC int execl(name, arg0) char *name; char *arg0; { return execve(name, &arg0, nullptr); } PUBLIC int execle(name, argv) char *name, *argv; { char **p; p = (char **) &argv; while (*p++) /* null statement */ ; return execve(name, &argv, *p); } PUBLIC int execv(name, argv) char *name, *argv[]; { return execve(name, argv, nullptr); } PUBLIC int execve(name, argv, envp) char *name; /* pointer to name of file to be executed */ char *argv[]; /* pointer to argument array */ char *envp[]; /* pointer to environment */ { char stack[MAX_ISTACK_BYTES]; char **argorg, **envorg, *hp, **ap, *p; int i, nargs, nenvps, stackbytes, ptrsize, offset; /* Count the argument pointers and environment pointers. */ nargs = 0; nenvps = 0; argorg = argv; envorg = envp; while (*argorg++ != NIL_PTR) nargs++; while (*envorg++ != NIL_PTR) nenvps++; ptrsize = sizeof(NIL_PTR); /* Prepare to set up the initial stack. */ hp = &stack[(nargs + nenvps + 3) * ptrsize]; if (hp + nargs + nenvps >= &stack[MAX_ISTACK_BYTES]) return(E2BIG); ap = (char **) stack; *ap++ = (char *) nargs; /* Prepare the argument pointers and strings. */ for (i = 0; i < nargs; i++) { offset = hp - stack; *ap++ = (char *) offset; p = *argv++; while (*p) { *hp++ = *p++; if (hp >= &stack[MAX_ISTACK_BYTES]) return(E2BIG); } *hp++ = (char) 0; } *ap++ = NIL_PTR; /* Prepare the environment pointers and strings. */ for (i = 0; i < nenvps; i++) { offset = hp - stack; *ap++ = (char *) offset; p = *envp++; while (*p) { *hp++ = *p++; if (hp >= &stack[MAX_ISTACK_BYTES]) return(E2BIG); } *hp++ = (char) 0; } *ap++ = NIL_PTR; stackbytes = ( ( (hp - stack) + ptrsize - 1)/ptrsize) * ptrsize; return callm1(MM_PROC_NR, EXEC, len(name), stackbytes, 0,name, stack,NIL_PTR); } PUBLIC execn(name) char *name; /* pointer to file to be exec'd */ { /* Special version used when there are no args and no environment. This call * is principally used by INIT, to avoid having to allocate MAX_ISTACK_BYTES. */ char stack[4]; stack[0] = 0; stack[1] = 0; stack[2] = 0; stack[3] = 0; return callm1(MM_PROC_NR, EXEC, len(name), 4, 0, name, stack, NIL_PTR); } kexit.cС ]Я Є‰#include "../include/lib.h" PUBLIC int exit(status) int status; { return callm1(MM, EXIT, status, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } fclose.cС ]Я Є:#include "../include/stdio.h" fclose(fp) FILE *fp; { register int i; for (i=0; i= NFILES) return(EOF); fflush(fp); close(fp->_fd); if ( testflag(fp,IOMYBUF) && fp->_buf ) free( fp->_buf ); free(fp); return(NULL); } fflush.cС ]Я Єo#include "../include/stdio.h" fflush(iop) FILE *iop; { int count; if ( testflag(iop,UNBUFF) || !testflag(iop,WRITEMODE) ) return(0); if ( iop->_count <= 0 ) return(0); count = write(iop->_fd,iop->_buf,iop->_count); if ( count == iop->_count) { iop->_count = 0; iop->_ptr = iop->_buf; return(count); } iop->_flags |= _ERR; return(EOF); } )fgets.cС ]Я Є<#include "../include/stdio.h" char *fgets(str, n, file) char *str; unsigned n; FILE *file; { register int ch; register char *ptr; ptr = str; while ( --n > 0 && (ch = getc(file)) != EOF){ *ptr++ = ch; if ( ch == '\n') break; } if (ch == EOF && ptr==str) return(NULL); *ptr = '\0'; return(str); } fopen.cС ]Я Є‡#include "../include/stdio.h" #define PMODE 0644 FILE *fopen(name,mode) char *name , *mode; { register int i; FILE *fp; char *malloc(); int fd, flags = 0; for (i = 0; _io_table[i] != 0 ; i++) if ( i >= NFILES ) return(NULL); switch(*mode){ case 'w': flags |= WRITEMODE; if (( fd = creat (name,PMODE)) < 0) return(NULL); break; case 'a': flags |= WRITEMODE; if (( fd = open(name,1)) < 0 ) return(NULL); lseek(fd,0L,2); break; case 'r': flags |= READMODE; if (( fd = open (name,0)) < 0 ) return(NULL); break; default: return(NULL); } if (( fp = (FILE *) malloc (sizeof( FILE))) == NULL ) return(NULL); fp->_count = 0; fp->_fd = fd; fp->_flags = flags; fp->_buf = malloc( BUFSIZ ); if ( fp->_buf == NULL ) fp->_flags |= UNBUFF; else fp->_flags |= IOMYBUF; fp->_ptr = fp->_buf; _io_table[i] = fp; return(fp); } Dfork.cС ]Я Єr#include "../include/lib.h" PUBLIC int fork() { return callm1(MM, FORK, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } fprintf.cС ]Я Є@#include "../include/stdio.h" fprintf (file, fmt, args) FILE *file; char *fmt; int args; { _doprintf (file, fmt, &args); if ( testflag(file,PERPRINTF) ) fflush(file); } printf (fmt, args) char *fmt; int args; { _doprintf (stdout, fmt, &args); if ( testflag(stdout,PERPRINTF) ) fflush(stdout); } fputs.cС ]Я Єq#include "../include/stdio.h" fputs(s,file) register char *s; FILE *file; { while ( *s ) putc(*s++,file); } ,fread.cС ]Я Єi#include "../include/stdio.h" fread(ptr, size, count, file) char *ptr; unsigned size, count; FILE *file; { register int c; unsigned ndone = 0, s; ndone = 0; if (size) while ( ndone < count ) { s = size; do { if ((c = getc(file)) != EOF) *ptr++ = (char) c; else return(ndone); } while (--s); ndone++; } return(ndone); } tfreopen.cС ]Я ЄК#include "../include/stdio.h" FILE *freopen(name,mode,stream) char *name,*mode; FILE *stream; { FILE *fopen(); if ( fclose(stream) != 0 ) return NULL; return fopen(name,mode); } fseek.cС ]Я Єi#include "../include/stdio.h" fseek(iop, offset, where) FILE *iop; long offset; { int count; long lseek(); long pos; iop->_flags &= ~(_EOF | _ERR); /* Clear both the end of file and error flags */ if ( testflag(iop,READMODE) ) { if ( where < 2 && iop->_buf && !testflag(iop,UNBUFF) ) { count = iop->_count; pos = offset; if ( where == 0 ) pos += count - lseek(fileno(iop), 0L,1) - 1; /*^^^ This caused the problem : - 1 corrected it */ else offset -= count; if ( count > 0 && pos <= count && pos >= iop->_buf - iop->_ptr ) { iop->_ptr += (int) pos; iop->_count -= (int) pos; return(0); } } pos = lseek(fileno(iop), offset, where); iop->_count = 0; } else if ( testflag(iop,WRITEMODE) ) { fflush(iop); pos = lseek(fileno(iop), offset, where); } return((pos == -1) ? -1 : 0 ); } fstat.cС ]Я ЄЇ#include "../include/lib.h" PUBLIC int fstat(fd, buffer) int fd; char *buffer; { int n; n = callm1(FS, FSTAT, fd, 0, 0, buffer, NIL_PTR, NIL_PTR); return(n); } ftell.cС ^Я ЄЂ#include "../include/stdio.h" long ftell(iop) FILE *iop; { long result; long lseek(); int adjust = 0; if ( testflag(iop,READMODE) ) adjust -= iop->_count; else if ( testflag(iop,WRITEMODE) && iop->_buf && !testflag(iop,UNBUFF)) adjust = iop->_ptr - iop->_buf; else return(-1); result = lseek(fileno(iop), 0L, 1); if ( result < 0 ) return ( result ); result += (long) adjust; return(result); } fwrite.cС ^Я ЄF#include "../include/stdio.h" fwrite(ptr, size, count, file) unsigned size, count; char *ptr; FILE *file; { unsigned s; unsigned ndone = 0; if (size) while ( ndone < count ) { s = size; do { putc(*ptr++, file); if (ferror(file)) return(ndone); } while (--s); ndone++; } return(ndone); } getc.cС ^Я ЄM#include "../include/stdio.h" getc(iop) FILE *iop; { int ch; if ( testflag(iop, (_EOF | _ERR ))) return (EOF); if ( !testflag(iop, READMODE) ) return (EOF); if (--iop->_count <= 0){ if ( testflag(iop, UNBUFF) ) iop->_count = read(iop->_fd,&ch,1); else iop->_count = read(iop->_fd,iop->_buf,BUFSIZ); if (iop->_count <= 0){ if (iop->_count == 0) iop->_flags |= _EOF; else iop->_flags |= _ERR; return (EOF); } else iop->_ptr = iop->_buf; } if (testflag(iop,UNBUFF)) return (ch & CMASK); else return (*iop->_ptr++ & CMASK); } lgetegid.cС ^Я ЄЗ#include "../include/lib.h" PUBLIC gid getegid() { int k; k = callm1(MM, GETGID, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); if (k < 0) return ( (gid) k); return( (gid) M.m2_i1); } >getenv.cС ^Я Єџchar *getenv(name) register char *name; { extern char **environ; register char **v = environ, *p, *q; while ((p = *v++) != 0) { q = name; while (*q && *q++ == *p++) /* nothing */ ; if (*q || *p != '=') continue; return(p+1); } return(0); } geteuid.cС _Я ЄЗ#include "../include/lib.h" PUBLIC uid geteuid() { int k; k = callm1(MM, GETUID, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); if (k < 0) return ( (uid) k); return ((uid) M.m2_i1); } +getgid.cС _Я Є#include "../include/lib.h" PUBLIC gid getgid() { int k; k = callm1(MM, GETGID, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); return( (gid) k); } getgrent.cС _Я Єђ/* * get entry from group file * * By: Patrick van Kleef */ #include "../include/grp.h" #define PRIVATE static PRIVATE char _gr_file[] = "/etc/group"; PRIVATE char _grbuf[256]; PRIVATE char _buffer[1024]; PRIVATE char *_pnt; PRIVATE char *_buf; PRIVATE int _gfd = -1; PRIVATE int _bufcnt; PRIVATE struct group grp; setgrent () { if (_gfd >= 0) lseek (_gfd, 0L, 0); else _gfd = open (_gr_file, 0); _bufcnt = 0; return (_gfd); } endgrent () { if (_gfd >= 0) close (_gfd); _gfd = -1; _bufcnt = 0; } static getline () { if (_gfd < 0 && setgrent () < 0) return (0); _buf = _grbuf; do { if (--_bufcnt <= 0){ if ((_bufcnt = read (_gfd, _buffer, 1024)) <= 0) return (0); else _pnt = _buffer; } *_buf++ = *_pnt++; } while (*_pnt != '\n'); _pnt++; _bufcnt--; *_buf = 0; _buf = _grbuf; return (1); } static skip_period () { while (*_buf != ':') _buf++; *_buf++ = '\0'; } struct group *getgrent () { if (getline () == 0) return (0); grp.name = _buf; skip_period (); grp.passwd = _buf; skip_period (); grp.gid = atoi (_buf); skip_period (); return (&grp); } struct group *getgrnam (name) char *name; { struct group *grp; setgrent (); while ((grp = getgrent ()) != 0) if (!strcmp (grp -> name, name)) break; endgrent (); if (grp != 0) return (grp); else return (0); } struct group *getgrgid (gid) int gid; { struct group *grp; setgrent (); while ((grp = getgrent ()) != 0) if (grp -> gid == gid) break; endgrent (); if (grp != 0) return (grp); else return (0); } getpass.cС _Я Є˜#include "../h/sgtty.h" static char pwdbuf[9]; char * getpass(prompt) char *prompt; { int i = 0; struct sgttyb tty; prints(prompt); ioctl(0, TIOCGETP, &tty); tty.sg_flags = 06020; ioctl(0, TIOCSETP, &tty); i = read(0, pwdbuf, 9); while (pwdbuf[i - 1] != '\n') read(0, &pwdbuf[i - 1], 1); pwdbuf[i - 1] = '\0'; tty.sg_flags = 06030; ioctl(0, TIOCSETP, &tty); prints("\n"); return(pwdbuf); } getpid.cС _Я Єv#include "../include/lib.h" PUBLIC int getpid() { return callm1(MM, GETPID, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } getpwent.cС _Я Єб/* * get entry from password file * * By Patrick van Kleef * */ #include "../include/pwd.h" #define PRIVATE static PRIVATE char _pw_file[] = "/etc/passwd"; PRIVATE char _pwbuf[256]; PRIVATE char _buffer[1024]; PRIVATE char *_pnt; PRIVATE char *_buf; PRIVATE int _pw = -1; PRIVATE int _bufcnt; PRIVATE struct passwd pwd; setpwent() { if (_pw >= 0) lseek (_pw, 0L, 0); else _pw = open (_pw_file, 0); _bufcnt = 0; return (_pw); } endpwent () { if (_pw >= 0) close (_pw); _pw = -1; _bufcnt = 0; } static getline () { if (_pw < 0 && setpwent () < 0) return (0); _buf = _pwbuf; do { if (--_bufcnt <= 0){ if ((_bufcnt = read (_pw, _buffer, 1024)) <= 0) return (0); else _pnt = _buffer; } *_buf++ = *_pnt++; } while (*_pnt != '\n'); _pnt++; _bufcnt--; *_buf = 0; _buf = _pwbuf; return (1); } static skip_period () { while (*_buf != ':') _buf++; *_buf++ = '\0'; } struct passwd *getpwent () { if (getline () == 0) return (0); pwd.pw_name = _buf; skip_period (); pwd.pw_passwd = _buf; skip_period (); pwd.pw_uid = atoi (_buf); skip_period (); pwd.pw_gid = atoi (_buf); skip_period (); pwd.pw_gecos = _buf; skip_period (); pwd.pw_dir = _buf; skip_period (); pwd.pw_shell = _buf; return (&pwd); } struct passwd *getpwnam (name) char *name; { struct passwd *pwd; setpwent (); while ((pwd = getpwent ()) != 0) if (!strcmp (pwd -> pw_name, name)) break; endpwent (); if (pwd != 0) return (pwd); else return (0); } struct passwd *getpwuid (uid) int uid; { struct passwd *pwd; setpwent (); while ((pwd = getpwent ()) != 0) if (pwd -> pw_uid == uid) break; endpwent (); if (pwd != 0) return (pwd); else return (0); } pgets.cС `Я Єћ#include "../include/stdio.h" char *gets(str) char *str; { register int ch; register char *ptr; ptr = str; while ((ch = getc(stdin)) != EOF && ch != '\n') *ptr++ = ch; if (ch == EOF && ptr==str) return(NULL); *ptr = '\0'; return(str); } agetuid.cС `Я Є#include "../include/lib.h" PUBLIC uid getuid() { int k; k = callm1(MM, GETUID, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); return( (uid) k); } index.cС aЯ Єqchar *index(s, c) register char *s, c; { do { if (*s == c) return(s); } while (*s++ != 0); return(0); } Iioctl.cС aЯ Єё#include "../include/lib.h" #include "../h/com.h" #include "../include/sgtty.h" PUBLIC int ioctl(fd, request, u) int fd; int request; union { struct sgttyb *argp; struct tchars *argt; } u; { int n; long erase, kill, intr, quit, xon, xoff, eof, brk; M.TTY_REQUEST = request; M.TTY_LINE = fd; switch(request) { case TIOCSETP: erase = u.argp->sg_erase & 0377; kill = u.argp->sg_kill & 0377; M.TTY_SPEK = (erase << 8) | kill; M.TTY_FLAGS = u.argp->sg_flags; n = callx(FS, IOCTL); return(n); case TIOCSETC: intr = u.argt->t_intrc & 0377; quit = u.argt->t_quitc & 0377; xon = u.argt->t_startc & 0377; xoff = u.argt->t_stopc & 0377; eof = u.argt->t_eofc & 0377; brk = u.argt->t_brkc & 0377; /* not used at the moment */ M.TTY_SPEK = (intr<<24) | (quit<<16) | (xon<<8) | (xoff<<0); M.TTY_FLAGS = (eof<<8) | (brk<<0); n = callx(FS, IOCTL); return(n); case TIOCGETP: n = callx(FS, IOCTL); u.argp->sg_erase = (M.TTY_SPEK >> 8) & 0377; u.argp->sg_kill = (M.TTY_SPEK >> 0) & 0377; u.argp->sg_flags = M.TTY_FLAGS; return(n); case TIOCGETC: n = callx(FS, IOCTL); u.argt->t_intrc = (M.TTY_SPEK >> 24) & 0377; u.argt->t_quitc = (M.TTY_SPEK >> 16) & 0377; u.argt->t_startc = (M.TTY_SPEK >> 8) & 0377; u.argt->t_stopc = (M.TTY_SPEK >> 0) & 0377; u.argt->t_eofc = (M.TTY_FLAGS >> 8) & 0377; u.argt->t_brkc = (M.TTY_FLAGS >> 8) & 0377; return(n); default: n = -1; errno = -(EINVAL); return(n); } } isatty.cС aЯ ЄЁ#include "../include/stat.h" int isatty(fd) int fd; { struct stat s; fstat(fd, &s); if ( (s.st_mode&S_IFMT) == S_IFCHR) return(1); else return(0); } >itoa.cС aЯ ЄЖ/* Integer to ASCII for signed decimal integers. */ static int next; static char qbuf[8]; char *itoa(n) int n; { register int r, k; int flag = 0; next = 0; if (n < 0) { qbuf[next++] = '-'; n = -n; } if (n == 0) { qbuf[next++] = '0'; } else { k = 10000; while (k > 0) { r = n/k; if (flag || r > 0) { qbuf[next++] = '0' + r; flag = 1; } n -= r * k; k = k/10; } } qbuf[next] = 0; return(qbuf); } kill.cС aЯ Єи#include "../include/lib.h" PUBLIC int kill(proc, sig) int proc; /* which process is to be sent the signal */ int sig; /* signal number */ { return callm1(MM, KILL, proc, sig, 0, NIL_PTR, NIL_PTR, NIL_PTR); } link.cС aЯ Є#include "../include/lib.h" PUBLIC int link(name, name2) char *name, *name2; { return callm1(FS, LINK, len(name), len(name2), 0, name, name2, NIL_PTR); } lseek.cС bЯ Є#include "../include/lib.h" PUBLIC long lseek(fd, offset, whence) int fd; long offset; int whence; { int k; M.m2_i1 = fd; M.m2_l1 = offset; M.m2_i2 = whence; k = callx(FS, LSEEK); if (k != OK) return( (long) k); /* send itself failed */ return(M.m2_l1); } malloc.cЩ ЂШ Є. #define CLICK_SIZE 16 typedef unsigned short vir_bytes; extern bcopy(); #define ALIGN(x, a) (((x) + (a - 1)) & ~(a - 1)) #define BUSY 1 #define NEXT(p) (* (char **) (p)) extern char *sbrk(); static char *bottom, *top; static grow(len) unsigned len; { register char *p; p = (char *) ALIGN((vir_bytes) top + sizeof(char *) + len, CLICK_SIZE) - sizeof(char *); if (p < top || brk(p) < 0) return(0); top = p; for (p = bottom; NEXT(p) != 0; p = (char *) (* (vir_bytes *) p & ~BUSY)) ; NEXT(p) = top; NEXT(top) = 0; return(1); } char *malloc(size) unsigned size; { register char *p, *next, *new; register unsigned len = ALIGN(size, sizeof(char *)) + sizeof(char *); if ((p = bottom) == 0) { top = bottom = p = sbrk(sizeof(char *)); NEXT(top) = 0; } while ((next = NEXT(p)) != 0) if ((vir_bytes) next & BUSY) /* already in use */ p = (char *) ((vir_bytes) next & ~BUSY); else { while ((new = NEXT(next)) != 0 && !((vir_bytes) new & BUSY)) next = new; if (next - p >= len) { /* fits */ if ((new = p + len) < next) /* too big */ NEXT(new) = next; NEXT(p) = (char *) ((vir_bytes) new | BUSY); return(p + sizeof(char *)); } p = next; } return grow(len) ? malloc(size) : 0; } char *realloc(old, size) char *old; unsigned size; { register char *p = old - sizeof(char *), *next, *new; register unsigned len = ALIGN(size, sizeof(char *)) + sizeof(char *), n; next = (char *) (* (vir_bytes *) p & ~BUSY); n = next - old; /* old size */ while ((new = NEXT(next)) != 0 && !((vir_bytes) new & BUSY)) next = new; if (next - p >= len) { /* does it still fit */ if ((new = p + len) < next) { /* even too big */ NEXT(new) = next; NEXT(p) = (char *) ((vir_bytes) new | BUSY); } else NEXT(p) = (char *) ((vir_bytes) next | BUSY); return(old); } if ((new = malloc(size)) == 0) /* it didn't fit */ return(0); bcopy(old, new, n); /* n < size */ * (vir_bytes *) p &= ~BUSY; return(new); } char *calloc(m,size) unsigned m,size; { char *malloc(); char *cp; register int i; register char *temp; i = m*size; if ((cp=malloc(i))==(char *)0) return (char *)0; /* malloc succeeded--clear allocated memory */ for (temp = cp ; i-- ; ) *temp++ = '\0'; return cp; } free(p) char *p; { * (vir_bytes *) (p - sizeof(char *)) &= ~BUSY; } message.cС bЯ Є;#include "../h/const.h" #include "../h/type.h" message M; mknod.cС bЯ ЄЋ#include "../include/lib.h" PUBLIC int mknod(name, mode, addr) char *name; int mode, addr; { return callm1(FS, MKNOD, len(name), mode, addr, name, NIL_PTR, NIL_PTR); } )mktemp.cС bЯ ЄЄ/* mktemp - make a name for a temporary file */ char *mktemp(template) char *template; { int pid, k; char *p; pid = getpid(); /* get process id as semi-unique number */ p = template; while (*p++) ; /* find end of string */ p--; /* backup to last character */ /* Replace XXXXXX at end of template with pid. */ while (*--p == 'X') { *p = '0' + (pid % 10); pid = pid/10; } return(template); } mount.cС bЯ ЄР#include "../include/lib.h" PUBLIC int mount(special, name, rwflag) char *name, *special; int rwflag; { return callm1(FS, MOUNT, len(special), len(name), rwflag, special, name, NIL_PTR); } open.cС bЯ Єz#include "../include/lib.h" PUBLIC int open(name, mode) char* name; int mode; { return callm3(FS, OPEN, mode, name); } pause.cС bЯ Єt#include "../include/lib.h" PUBLIC int pause() { return callm1(MM, PAUSE, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } perror.cС bЯ ЄЉ/* perror(s) print the current error message. */ #include "../h/error.h" extern int errno; char *error_message[NERROR+1] = { "Error 0", "Not owner", "No such file or directory", "No such process", "Interrupted system call", "I/O error", "No such device or address", "Arg list too long", "Exec format error", "Bad file number", "No children", "No more processes", "Not enough core", "Permission denied", "Bad address", "Block device required", "Mount device busy", "File exists", "Cross-device link", "No such device", "Not a directory", "Is a directory", "Invalid argument", "File table overflow", "Too many open files", "Not a typewriter", "Text file busy", "File too large", "No space left on device", "Illegal seek", "Read-only file system", "Too many links", "Broken pipe", "Math argument", "Result too large" }; perror(s) char *s; { if (errno < 0 || errno > NERROR) { write(2, "Invalid errno\n", 14); } else { write(2, s, slen(s)); write(2, ": ", 2); write(2, error_message[errno], slen(error_message[errno])); write(2, "\n", 1); } } static int slen(s) char *s; { int k = 0; while (*s++) k++; return(k); } pipe.cС bЯ Єт#include "../include/lib.h" PUBLIC int pipe(fild) int fild[2]; { int k; k = callm1(FS, PIPE, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); if (k >= 0) { fild[0] = M.m1_i1; fild[1] = M.m1_i2; return(0); } else return(k); } printdat.cС cЯ Єs#include "../include/stdio.h" char __stdin[BUFSIZ]; char __stdout[BUFSIZ]; struct _io_buf _stdin = { 0, 0, READMODE , __stdin, __stdin }; struct _io_buf _stdout = { 1, 0, WRITEMODE + PERPRINTF, __stdout, __stdout }; struct _io_buf _stderr = { 2, 0, WRITEMODE + UNBUFF, NULL, NULL }; struct _io_buf *_io_table[NFILES] = { &_stdin, &_stdout, &_stderr, 0 }; sprintk.c!Ѕq Є/* This is a special version of printf. It is used only by the operating * system itself, and should never be included in user programs. The name * printk never appears in the operating system, because the macro printf * has been defined as printk there. */ #define MAXDIGITS 12 printk(s, arglist) char *s; int *arglist; { int w, k, r, *valp; unsigned u; char **pp; long l, *lp; char a[MAXDIGITS], *p, *p1, c; valp = (int *) &arglist; while (*s != '\0') { if (*s != '%') { putc(*s++); continue; } w = 0; s++; while (*s >= '0' && *s <= '9') { w = 10 * w + (*s - '0'); s++; } lp = (long *) valp; pp = (char **) valp; switch(*s) { case 'd': k = *valp++; l = k; r = 10; break; case 'o': k = *valp++; u = k; l = u; r = 8; break; case 'x': k = *valp++; u = k; l = u; r = 16; break; case 'D': l = *lp++; r = 10; valp = (int *) lp; break; case 'O': l = *lp++; r = 8; valp = (int *) lp; break; case 'X': l = *lp++; r = 16; valp = (int *) lp; break; case 'c': k = *valp++; putc(k); s++; continue; case 's': p = *pp++; valp = (int *) pp; p1 = p; while(c = *p++) putc(c); s++; if ( (k = w - (p-p1-1)) > 0) while (k--) putc(' '); continue; default: putc('%'); putc(*s++); continue; } k = bintoascii(l, r, a); if ( (r = w - k) > 0) while(r--) putc(' '); for (r = k - 1; r >= 0; r--) putc(a[r]); s++; } } static int bintoascii(num, radix, a) long num; int radix; char a[MAXDIGITS]; { int i, n, hit, negative; negative = 0; if (num == 0) {a[0] = '0'; return(1);} if (num < 0 && radix == 10) {num = -num; negative++;} for (n = 0; n < MAXDIGITS; n++) a[n] = 0; n = 0; do { if (radix == 10) {a[n] = num % 10; num = (num -a[n])/10;} if (radix == 8) {a[n] = num & 0x7; num = (num >> 3) & 0x1FFFFFFF;} if (radix == 16) {a[n] = num & 0xF; num = (num >> 4) & 0x0FFFFFFF;} n++; } while (num != 0); /* Convert to ASCII. */ hit = 0; for (i = n - 1; i >= 0; i--) { if (a[i] == 0 && hit == 0) { a[i] = ' '; } else { if (a[i] < 10) a[i] += '0'; else a[i] += 'A' - 10; hit++; } } if (negative) a[n++] = '-'; return(n); } Gprints.cС cЯ Є…/* prints() is like printf(), except that it can only handle %s and %c. It * cannot print any of the numeric types such as %d, %o, etc. It has the * advantage of not requiring the runtime code for converting binary numbers * to ASCII, which saves 1K bytes in the object program. Since many of the * small utilities do not need numeric printing, they all use prints. */ #define TRUNC_SIZE 128 char Buf[TRUNC_SIZE], *Bufp; #define OUT 1 prints(s, arglist) register char *s; int *arglist; { register w; int k, r, *valp; char *p, *p1, c; Bufp = Buf; valp = (int *) &arglist; while (*s != '\0') { if (*s != '%') { put(*s++); continue; } w = 0; s++; while (*s >= '0' && *s <= '9') { w = 10 * w + (*s - '0'); s++; } switch(*s) { case 'c': k = *valp++; put(k); s++; continue; case 's': p = (char *) *valp++; p1 = p; while(c = *p++) put(c); s++; if ( (k = w - (p-p1-1)) > 0) while (k--) put(' '); continue; default: put('%'); put(*s++); continue; } } write(OUT, Buf, Bufp - Buf); /* write everything in one blow. */ } static put(c) char c; { if (Bufp < &Buf[TRUNC_SIZE]) *Bufp++ = c; } +putc.cС cЯ Є‘#include "../include/stdio.h" putc(ch, iop) char ch; FILE *iop; { int n, didwrite = 0; if (testflag(iop, (_ERR | _EOF))) return (EOF); if ( !testflag(iop,WRITEMODE)) return(EOF); if ( testflag(iop,UNBUFF)){ n = write(iop->_fd,&ch,1); iop->_count = 1; didwrite++; } else{ *iop->_ptr++ = ch; if ((++iop->_count) >= BUFSIZ && !testflag(iop,STRINGS) ){ n = write(iop->_fd,iop->_buf,iop->_count); iop->_ptr = iop->_buf; didwrite++; } } if (didwrite){ if (n<=0 || iop->_count != n){ if (n < 0) iop->_flags |= _ERR; else iop->_flags |= _EOF; return (EOF); } iop->_count=0; } return(ch & CMASK); } rand.cС dЯ Є|static long seed = 1L; int rand() { seed = (1103515245L * seed + 12345) & 0x7FFFFFFF; return((int) (seed & 077777)); } read.cС dЯ ЄО#include "../include/lib.h" PUBLIC int read(fd, buffer, nbytes) int fd; char *buffer; int nbytes; { int n; n = callm1(FS, READ, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR); return(n); } regexp.cС dЯ Єl/* * regcomp and regexec -- regsub and regerror are elsewhere * * Copyright (c) 1986 by University of Toronto. * Written by Henry Spencer. Not derived from licensed software. * * Permission is granted to anyone to use this software for any * purpose on any computer system, and to redistribute it freely, * subject to the following restrictions: * * 1. The author is not responsible for the consequences of use of * this software, no matter how awful, even if they arise * from defects in it. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. * * 3. Altered versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * Beware that some of this code is subtly aware of the way operator * precedence is structured in regular expressions. Serious changes in * regular-expression syntax might require a total rethink. * * The third parameter to regexec was added by Martin C. Atkins. * Andy Tanenbaum also made some changes. */ #include "../include/stdio.h" #include "../include/regexp.h" /* * The first byte of the regexp internal "program" is actually this magic * number; the start node begins in the second byte. */ #define MAGIC 0234 /* * The "internal use only" fields in regexp.h are present to pass info from * compile to execute that permits the execute phase to run lots faster on * simple cases. They are: * * regstart char that must begin a match; '\0' if none obvious * reganch is the match anchored (at beginning-of-line only)? * regmust string (pointer into program) that match must include, or NULL * regmlen length of regmust string * * Regstart and reganch permit very fast decisions on suitable starting points * for a match, cutting down the work a lot. Regmust permits fast rejection * of lines that cannot possibly match. The regmust tests are costly enough * that regcomp() supplies a regmust only if the r.e. contains something * potentially expensive (at present, the only such thing detected is * or + * at the start of the r.e., which can involve a lot of backup). Regmlen is * supplied because the test in regexec() needs it and regcomp() is computing * it anyway. */ /* * Structure for regexp "program". This is essentially a linear encoding * of a nondeterministic finite-state machine (aka syntax charts or * "railroad normal form" in parsing technology). Each node is an opcode * plus a "next" pointer, possibly plus an operand. "Next" pointers of * all nodes except BRANCH implement concatenation; a "next" pointer with * a BRANCH on both ends of it is connecting two alternatives. (Here we * have one of the subtle syntax dependencies: an individual BRANCH (as * opposed to a collection of them) is never concatenated with anything * because of operator precedence.) The operand of some types of node is * a literal string; for others, it is a node leading into a sub-FSM. In * particular, the operand of a BRANCH node is the first node of the branch. * (NB this is *not* a tree structure: the tail of the branch connects * to the thing following the set of BRANCHes.) The opcodes are: */ /* definition number opnd? meaning */ #define END 0 /* no End of program. */ #define BOL 1 /* no Match "" at beginning of line. */ #define EOL 2 /* no Match "" at end of line. */ #define ANY 3 /* no Match any one character. */ #define ANYOF 4 /* str Match any character in this string. */ #define ANYBUT 5 /* str Match any character not in this string. */ #define BRANCH 6 /* node Match this alternative, or the next... */ #define BACK 7 /* no Match "", "next" ptr points backward. */ #define EXACTLY 8 /* str Match this string. */ #define NOTHING 9 /* no Match empty string. */ #define STAR 10 /* node Match this (simple) thing 0 or more times. */ #define PLUS 11 /* node Match this (simple) thing 1 or more times. */ #define OPEN 20 /* no Mark this point in input as start of #n. */ /* OPEN+1 is number 1, etc. */ #define CLOSE 30 /* no Analogous to OPEN. */ /* * Opcode notes: * * BRANCH The set of branches constituting a single choice are hooked * together with their "next" pointers, since precedence prevents * anything being concatenated to any individual branch. The * "next" pointer of the last BRANCH in a choice points to the * thing following the whole choice. This is also where the * final "next" pointer of each individual branch points; each * branch starts with the operand node of a BRANCH node. * * BACK Normal "next" pointers all implicitly point forward; BACK * exists to make loop structures possible. * * STAR,PLUS '?', and complex '*' and '+', are implemented as circular * BRANCH structures using BACK. Simple cases (one character * per match) are implemented with STAR and PLUS for speed * and to minimize recursive plunges. * * OPEN,CLOSE ...are numbered at compile time. */ /* * A node is one char of opcode followed by two chars of "next" pointer. * "Next" pointers are stored as two 8-bit pieces, high order first. The * value is a positive offset from the opcode of the node containing it. * An operand, if any, simply follows the node. (Note that much of the * code generation knows about this implicit relationship.) * * Using two bytes for the "next" pointer is vast overkill for most things, * but allows patterns to get big without disasters. */ #define OP(p) (*(p)) #define NEXT(p) (((*((p)+1)&0377)<<8) + *((p)+2)&0377) #define OPERAND(p) ((p) + 3) /* * Utility definitions. */ #ifndef CHARBITS #define UCHARAT(p) ((int)*(unsigned char *)(p)) #else #define UCHARAT(p) ((int)*(p)&CHARBITS) #endif #define FAIL(m) { regerror(m); return(NULL); } #define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') #define META "^$.[()|?+*\\" /* * Flags to be passed up and down. */ #define HASWIDTH 01 /* Known never to match null string. */ #define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ #define SPSTART 04 /* Starts with * or +. */ #define WORST 0 /* Worst case. */ /* * Global work variables for regcomp(). */ static char *regparse; /* Input-scan pointer. */ static int regnpar; /* () count. */ static char regdummy; static char *regcode; /* Code-emit pointer; ®dummy = don't. */ static long regsize; /* Code size. */ /* * Forward declarations for regcomp()'s friends. */ #ifndef STATIC #define STATIC static #endif STATIC char *reg(); STATIC char *regbranch(); STATIC char *regpiece(); STATIC char *regatom(); STATIC char *regnode(); STATIC char *regnext(); STATIC void regc(); STATIC void reginsert(); STATIC void regtail(); STATIC void regoptail(); STATIC int strcspn(); /* - regcomp - compile a regular expression into internal code * * We can't allocate space until we know how big the compiled form will be, * but we can't compile it (and thus know how big it is) until we've got a * place to put the code. So we cheat: we compile it twice, once with code * generation turned off and size counting turned on, and once "for real". * This also means that we don't allocate space until we are sure that the * thing really will compile successfully, and we never have to move the * code and thus invalidate pointers into it. (Note that it has to be in * one piece because free() must be able to free it all.) * * Beware that the optimization-preparation code in here knows about some * of the structure of the compiled regexp. */ regexp * regcomp(exp) char *exp; { register regexp *r; register char *scan; register char *longest; register int len; int flags; extern char *malloc(); if (exp == NULL) FAIL("NULL argument"); /* First pass: determine size, legality. */ regparse = exp; regnpar = 1; regsize = 0L; regcode = ®dummy; regc(MAGIC); if (reg(0, &flags) == NULL) return(NULL); /* Small enough for pointer-storage convention? */ if (regsize >= 32767L) /* Probably could be 65535L. */ FAIL("regexp too big"); /* Allocate space. */ r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); if (r == NULL) FAIL("out of space"); /* Second pass: emit code. */ regparse = exp; regnpar = 1; regcode = r->program; regc(MAGIC); if (reg(0, &flags) == NULL) return(NULL); /* Dig out information for optimizations. */ r->regstart = '\0'; /* Worst-case defaults. */ r->reganch = 0; r->regmust = NULL; r->regmlen = 0; scan = r->program+1; /* First BRANCH. */ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ scan = OPERAND(scan); /* Starting-point info. */ if (OP(scan) == EXACTLY) r->regstart = *OPERAND(scan); else if (OP(scan) == BOL) r->reganch++; /* * If there's something expensive in the r.e., find the * longest literal string that must appear and make it the * regmust. Resolve ties in favor of later strings, since * the regstart check works with the beginning of the r.e. * and avoiding duplication strengthens checking. Not a * strong reason, but sufficient in the absence of others. */ if (flags&SPSTART) { longest = NULL; len = 0; for (; scan != NULL; scan = regnext(scan)) if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { longest = OPERAND(scan); len = strlen(OPERAND(scan)); } r->regmust = longest; r->regmlen = len; } } return(r); } /* - reg - regular expression, i.e. main body or parenthesized thing * * Caller must absorb opening parenthesis. * * Combining parenthesis handling with the base level of regular expression * is a trifle forced, but the need to tie the tails of the branches to what * follows makes it hard to avoid. */ static char * reg(paren, flagp) int paren; /* Parenthesized? */ int *flagp; { register char *ret; register char *br; register char *ender; register int parno; int flags; *flagp = HASWIDTH; /* Tentatively. */ /* Make an OPEN node, if parenthesized. */ if (paren) { if (regnpar >= NSUBEXP) FAIL("too many ()"); parno = regnpar; regnpar++; ret = regnode(OPEN+parno); } else ret = NULL; /* Pick up the branches, linking them together. */ br = regbranch(&flags); if (br == NULL) return(NULL); if (ret != NULL) regtail(ret, br); /* OPEN -> first. */ else ret = br; if (!(flags&HASWIDTH)) *flagp &= ~HASWIDTH; *flagp |= flags&SPSTART; while (*regparse == '|') { regparse++; br = regbranch(&flags); if (br == NULL) return(NULL); regtail(ret, br); /* BRANCH -> BRANCH. */ if (!(flags&HASWIDTH)) *flagp &= ~HASWIDTH; *flagp |= flags&SPSTART; } /* Make a closing node, and hook it on the end. */ ender = regnode((paren) ? CLOSE+parno : END); regtail(ret, ender); /* Hook the tails of the branches to the closing node. */ for (br = ret; br != NULL; br = regnext(br)) regoptail(br, ender); /* Check for proper termination. */ if (paren && *regparse++ != ')') { FAIL("unmatched ()"); } else if (!paren && *regparse != '\0') { if (*regparse == ')') { FAIL("unmatched ()"); } else FAIL("junk on end"); /* "Can't happen". */ /* NOTREACHED */ } return(ret); } /* - regbranch - one alternative of an | operator * * Implements the concatenation operator. */ static char * regbranch(flagp) int *flagp; { register char *ret; register char *chain; register char *latest; int flags; *flagp = WORST; /* Tentatively. */ ret = regnode(BRANCH); chain = NULL; while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { latest = regpiece(&flags); if (latest == NULL) return(NULL); *flagp |= flags&HASWIDTH; if (chain == NULL) /* First piece. */ *flagp |= flags&SPSTART; else regtail(chain, latest); chain = latest; } if (chain == NULL) /* Loop ran zero times. */ (void) regnode(NOTHING); return(ret); } /* - regpiece - something followed by possible [*+?] * * Note that the branching code sequences used for ? and the general cases * of * and + are somewhat optimized: they use the same NOTHING node as * both the endmarker for their branch list and the body of the last branch. * It might seem that this node could be dispensed with entirely, but the * endmarker role is not redundant. */ static char * regpiece(flagp) int *flagp; { register char *ret; register char op; register char *next; int flags; ret = regatom(&flags); if (ret == NULL) return(NULL); op = *regparse; if (!ISMULT(op)) { *flagp = flags; return(ret); } if (!(flags&HASWIDTH) && op != '?') FAIL("*+ operand could be empty"); *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); if (op == '*' && (flags&SIMPLE)) reginsert(STAR, ret); else if (op == '*') { /* Emit x* as (x&|), where & means "self". */ reginsert(BRANCH, ret); /* Either x */ regoptail(ret, regnode(BACK)); /* and loop */ regoptail(ret, ret); /* back */ regtail(ret, regnode(BRANCH)); /* or */ regtail(ret, regnode(NOTHING)); /* null. */ } else if (op == '+' && (flags&SIMPLE)) reginsert(PLUS, ret); else if (op == '+') { /* Emit x+ as x(&|), where & means "self". */ next = regnode(BRANCH); /* Either */ regtail(ret, next); regtail(regnode(BACK), ret); /* loop back */ regtail(next, regnode(BRANCH)); /* or */ regtail(ret, regnode(NOTHING)); /* null. */ } else if (op == '?') { /* Emit x? as (x|) */ reginsert(BRANCH, ret); /* Either x */ regtail(ret, regnode(BRANCH)); /* or */ next = regnode(NOTHING); /* null. */ regtail(ret, next); regoptail(ret, next); } regparse++; if (ISMULT(*regparse)) FAIL("nested *?+"); return(ret); } /* - regatom - the lowest level * * Optimization: gobbles an entire sequence of ordinary characters so that * it can turn them into a single node, which is smaller to store and * faster to run. Backslashed characters are exceptions, each becoming a * separate node; the code is simpler that way and it's not worth fixing. */ static char * regatom(flagp) int *flagp; { register char *ret; int flags; *flagp = WORST; /* Tentatively. */ switch (*regparse++) { case '^': ret = regnode(BOL); break; case '$': ret = regnode(EOL); break; case '.': ret = regnode(ANY); *flagp |= HASWIDTH|SIMPLE; break; case '[': { register int class; register int classend; if (*regparse == '^') { /* Complement of range. */ ret = regnode(ANYBUT); regparse++; } else ret = regnode(ANYOF); if (*regparse == ']' || *regparse == '-') regc(*regparse++); while (*regparse != '\0' && *regparse != ']') { if (*regparse == '-') { regparse++; if (*regparse == ']' || *regparse == '\0') regc('-'); else { class = UCHARAT(regparse-2)+1; classend = UCHARAT(regparse); if (class > classend+1) FAIL("invalid [] range"); for (; class <= classend; class++) regc(class); regparse++; } } else regc(*regparse++); } regc('\0'); if (*regparse != ']') FAIL("unmatched []"); regparse++; *flagp |= HASWIDTH|SIMPLE; } break; case '(': ret = reg(1, &flags); if (ret == NULL) return(NULL); *flagp |= flags&(HASWIDTH|SPSTART); break; case '\0': case '|': case ')': FAIL("internal urp"); /* Supposed to be caught earlier. */ break; case '?': case '+': case '*': FAIL("?+* follows nothing"); break; case '\\': if (*regparse == '\0') FAIL("trailing \\"); ret = regnode(EXACTLY); regc(*regparse++); regc('\0'); *flagp |= HASWIDTH|SIMPLE; break; default: { register int len; register char ender; regparse--; len = strcspn(regparse, META); if (len <= 0) FAIL("internal disaster"); ender = *(regparse+len); if (len > 1 && ISMULT(ender)) len--; /* Back off clear of ?+* operand. */ *flagp |= HASWIDTH; if (len == 1) *flagp |= SIMPLE; ret = regnode(EXACTLY); while (len > 0) { regc(*regparse++); len--; } regc('\0'); } break; } return(ret); } /* - regnode - emit a node */ static char * /* Location. */ regnode(op) char op; { register char *ret; register char *ptr; ret = regcode; if (ret == ®dummy) { regsize += 3; return(ret); } ptr = ret; *ptr++ = op; *ptr++ = '\0'; /* Null "next" pointer. */ *ptr++ = '\0'; regcode = ptr; return(ret); } /* - regc - emit (if appropriate) a byte of code */ static void regc(b) char b; { if (regcode != ®dummy) *regcode++ = b; else regsize++; } /* - reginsert - insert an operator in front of already-emitted operand * * Means relocating the operand. */ static void reginsert(op, opnd) char op; char *opnd; { register char *src; register char *dst; register char *place; if (regcode == ®dummy) { regsize += 3; return; } src = regcode; regcode += 3; dst = regcode; while (src > opnd) *--dst = *--src; place = opnd; /* Op node, where operand used to be. */ *place++ = op; *place++ = '\0'; *place++ = '\0'; } /* - regtail - set the next-pointer at the end of a node chain */ static void regtail(p, val) char *p; char *val; { register char *scan; register char *temp; register int offset; if (p == ®dummy) return; /* Find last node. */ scan = p; for (;;) { temp = regnext(scan); if (temp == NULL) break; scan = temp; } if (OP(scan) == BACK) offset = scan - val; else offset = val - scan; *(scan+1) = (offset>>8)&0377; *(scan+2) = offset&0377; } /* - regoptail - regtail on operand of first argument; nop if operandless */ static void regoptail(p, val) char *p; char *val; { /* "Operandless" and "op != BRANCH" are synonymous in practice. */ if (p == NULL || p == ®dummy || OP(p) != BRANCH) return; regtail(OPERAND(p), val); } /* * regexec and friends */ /* * Global work variables for regexec(). */ static char *reginput; /* String-input pointer. */ static char *regbol; /* Beginning of input, for ^ check. */ static char **regstartp; /* Pointer to startp array. */ static char **regendp; /* Ditto for endp. */ /* * Forwards. */ STATIC int regtry(); STATIC int regmatch(); STATIC int regrepeat(); #ifdef DEBUG int regnarrate = 0; void regdump(); STATIC char *regprop(); #endif /* - regexec - match a regexp against a string */ int regexec(prog, string, bolflag) register regexp *prog; register char *string; int bolflag; { register char *s; extern char *strchr(); /* Be paranoid... */ if (prog == NULL || string == NULL) { regerror("NULL parameter"); return(0); } /* Check validity of program. */ if (UCHARAT(prog->program) != MAGIC) { regerror("corrupted program"); return(0); } /* If there is a "must appear" string, look for it. */ if (prog->regmust != NULL) { s = string; while ((s = strchr(s, prog->regmust[0])) != NULL) { if (strncmp(s, prog->regmust, prog->regmlen) == 0) break; /* Found it. */ s++; } if (s == NULL) /* Not present. */ return(0); } /* Mark beginning of line for ^ . */ if(bolflag) regbol = string; else regbol = NULL; /* Simplest case: anchored match need be tried only once. */ if (prog->reganch) return(regtry(prog, string)); /* Messy cases: unanchored match. */ s = string; if (prog->regstart != '\0') /* We know what char it must start with. */ while ((s = strchr(s, prog->regstart)) != NULL) { if (regtry(prog, s)) return(1); s++; } else /* We don't -- general case. */ do { if (regtry(prog, s)) return(1); } while (*s++ != '\0'); /* Failure. */ return(0); } /* - regtry - try match at specific point */ static int /* 0 failure, 1 success */ regtry(prog, string) regexp *prog; char *string; { register int i; register char **sp; register char **ep; reginput = string; regstartp = prog->startp; regendp = prog->endp; sp = prog->startp; ep = prog->endp; for (i = NSUBEXP; i > 0; i--) { *sp++ = NULL; *ep++ = NULL; } if (regmatch(prog->program + 1)) { prog->startp[0] = string; prog->endp[0] = reginput; return(1); } else return(0); } /* - regmatch - main matching routine * * Conceptually the strategy is simple: check to see whether the current * node matches, call self recursively to see whether the rest matches, * and then act accordingly. In practice we make some effort to avoid * recursion, in particular by going through "ordinary" nodes (that don't * need to know whether the rest of the match failed) by a loop instead of * by recursion. */ static int /* 0 failure, 1 success */ regmatch(prog) char *prog; { register char *scan; /* Current node. */ char *next; /* Next node. */ extern char *strchr(); scan = prog; #ifdef DEBUG if (scan != NULL && regnarrate) fprintf(stderr, "%s(\n", regprop(scan)); #endif while (scan != NULL) { #ifdef DEBUG if (regnarrate) fprintf(stderr, "%s...\n", regprop(scan)); #endif next = regnext(scan); switch (OP(scan)) { case BOL: if (reginput != regbol) return(0); break; case EOL: if (*reginput != '\0') return(0); break; case ANY: if (*reginput == '\0') return(0); reginput++; break; case EXACTLY: { register int len; register char *opnd; opnd = OPERAND(scan); /* Inline the first character, for speed. */ if (*opnd != *reginput) return(0); len = strlen(opnd); if (len > 1 && strncmp(opnd, reginput, len) != 0) return(0); reginput += len; } break; case ANYOF: if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) return(0); reginput++; break; case ANYBUT: if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) return(0); reginput++; break; case NOTHING: break; case BACK: break; case OPEN+1: case OPEN+2: case OPEN+3: case OPEN+4: case OPEN+5: case OPEN+6: case OPEN+7: case OPEN+8: case OPEN+9: { register int no; register char *save; no = OP(scan) - OPEN; save = reginput; if (regmatch(next)) { /* * Don't set startp if some later * invocation of the same parentheses * already has. */ if (regstartp[no] == NULL) regstartp[no] = save; return(1); } else return(0); } break; case CLOSE+1: case CLOSE+2: case CLOSE+3: case CLOSE+4: case CLOSE+5: case CLOSE+6: case CLOSE+7: case CLOSE+8: case CLOSE+9: { register int no; register char *save; no = OP(scan) - CLOSE; save = reginput; if (regmatch(next)) { /* * Don't set endp if some later * invocation of the same parentheses * already has. */ if (regendp[no] == NULL) regendp[no] = save; return(1); } else return(0); } break; case BRANCH: { register char *save; if (OP(next) != BRANCH) /* No choice. */ next = OPERAND(scan); /* Avoid recursion. */ else { do { save = reginput; if (regmatch(OPERAND(scan))) return(1); reginput = save; scan = regnext(scan); } while (scan != NULL && OP(scan) == BRANCH); return(0); /* NOTREACHED */ } } break; case STAR: case PLUS: { register char nextch; register int no; register char *save; register int min; /* * Lookahead to avoid useless match attempts * when we know what character comes next. */ nextch = '\0'; if (OP(next) == EXACTLY) nextch = *OPERAND(next); min = (OP(scan) == STAR) ? 0 : 1; save = reginput; no = regrepeat(OPERAND(scan)); while (no >= min) { /* If it could work, try it. */ if (nextch == '\0' || *reginput == nextch) if (regmatch(next)) return(1); /* Couldn't or didn't -- back up. */ no--; reginput = save + no; } return(0); } break; case END: return(1); /* Success! */ break; default: regerror("memory corruption"); return(0); break; } scan = next; } /* * We get here only if there's trouble -- normally "case END" is * the terminating point. */ regerror("corrupted pointers"); return(0); } /* - regrepeat - repeatedly match something simple, report how many */ static int regrepeat(p) char *p; { register int count = 0; register char *scan; register char *opnd; scan = reginput; opnd = OPERAND(p); switch (OP(p)) { case ANY: count = strlen(scan); scan += count; break; case EXACTLY: while (*opnd == *scan) { count++; scan++; } break; case ANYOF: while (*scan != '\0' && strchr(opnd, *scan) != NULL) { count++; scan++; } break; case ANYBUT: while (*scan != '\0' && strchr(opnd, *scan) == NULL) { count++; scan++; } break; default: /* Oh dear. Called inappropriately. */ regerror("internal foulup"); count = 0; /* Best compromise. */ break; } reginput = scan; return(count); } /* - regnext - dig the "next" pointer out of a node */ static char * regnext(p) register char *p; { register int offset; if (p == ®dummy) return(NULL); offset = NEXT(p); if (offset == 0) return(NULL); if (OP(p) == BACK) return(p-offset); else return(p+offset); } #ifdef DEBUG STATIC char *regprop(); /* - regdump - dump a regexp onto stdout in vaguely comprehensible form */ void regdump(r) regexp *r; { register char *s; register char op = EXACTLY; /* Arbitrary non-END op. */ register char *next; extern char *strchr(); s = r->program + 1; while (op != END) { /* While that wasn't END last time... */ op = OP(s); printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ next = regnext(s); if (next == NULL) /* Next ptr. */ printf("(0)"); else printf("(%d)", (s-r->program)+(next-s)); s += 3; if (op == ANYOF || op == ANYBUT || op == EXACTLY) { /* Literal string, where present. */ while (*s != '\0') { putchar(*s); s++; } s++; } putchar('\n'); } /* Header fields of interest. */ if (r->regstart != '\0') printf("start `%c' ", r->regstart); if (r->reganch) printf("anchored "); if (r->regmust != NULL) printf("must have \"%s\"", r->regmust); printf("\n"); } /* - regprop - printable representation of opcode */ static char * regprop(op) char *op; { register char *p; static char buf[50]; (void) strcpy(buf, ":"); switch (OP(op)) { case BOL: p = "BOL"; break; case EOL: p = "EOL"; break; case ANY: p = "ANY"; break; case ANYOF: p = "ANYOF"; break; case ANYBUT: p = "ANYBUT"; break; case BRANCH: p = "BRANCH"; break; case EXACTLY: p = "EXACTLY"; break; case NOTHING: p = "NOTHING"; break; case BACK: p = "BACK"; break; case END: p = "END"; break; case OPEN+1: case OPEN+2: case OPEN+3: case OPEN+4: case OPEN+5: case OPEN+6: case OPEN+7: case OPEN+8: case OPEN+9: sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); p = NULL; break; case CLOSE+1: case CLOSE+2: case CLOSE+3: case CLOSE+4: case CLOSE+5: case CLOSE+6: case CLOSE+7: case CLOSE+8: case CLOSE+9: sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); p = NULL; break; case STAR: p = "STAR"; break; case PLUS: p = "PLUS"; break; default: regerror("corrupted opcode"); break; } if (p != NULL) (void) strcat(buf, p); return(buf); } #endif /* * The following is provided for those people who do not have strcspn() in * their C libraries. They should get off their butts and do something * about it; at least one public-domain implementation of those (highly * useful) string routines has been published on Usenet. */ /* * strcspn - find length of initial segment of s1 consisting entirely * of characters not from s2 */ static int strcspn(s1, s2) char *s1; char *s2; { register char *scan1; register char *scan2; register int count; count = 0; for (scan1 = s1; *scan1 != '\0'; scan1++) { for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ if (*scan1 == *scan2++) return(count); count++; } return(count); } regsub.cС dЯ ЄI/* * regsub * * Copyright (c) 1986 by University of Toronto. * Written by Henry Spencer. Not derived from licensed software. * * Permission is granted to anyone to use this software for any * purpose on any computer system, and to redistribute it freely, * subject to the following restrictions: * * 1. The author is not responsible for the consequences of use of * this software, no matter how awful, even if they arise * from defects in it. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. * * 3. Altered versions must be plainly marked as such, and must not * be misrepresented as being the original software. */ #include "../include/stdio.h" #include "../include/regexp.h" /* * The first byte of the regexp internal "program" is actually this magic * number; the start node begins in the second byte. */ #define MAGIC 0234 #define CHARBITS 0377 #ifndef CHARBITS #define UCHARAT(p) ((int)*(unsigned char *)(p)) #else #define UCHARAT(p) ((int)*(p)&CHARBITS) #endif /* - regsub - perform substitutions after a regexp match */ regsub(prog, source, dest) regexp *prog; char *source; char *dest; { register char *src; register char *dst; register char c; register int no; register int len; extern char *strncpy(); if (prog == NULL || source == NULL || dest == NULL) { regerror("NULL parm to regsub"); return; } if (UCHARAT(prog->program) != MAGIC) { regerror("damaged regexp fed to regsub"); return; } src = source; dst = dest; while ((c = *src++) != '\0') { if (c == '&') no = 0; else if (c == '\\' && '0' <= *src && *src <= '9') no = *src++ - '0'; else no = -1; if (no < 0) { /* Ordinary character. */ if (c == '\\' && (*src == '\\' || *src == '&')) c = *src++; *dst++ = c; } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { len = prog->endp[no] - prog->startp[no]; strncpy(dst, prog->startp[no], len); dst += len; if (len !=0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ regerror("damaged match string"); return; } } } *dst++ = '\0'; } =rindex.cС dЯ Єœchar *rindex(s, c) register char *s, c; { register char *result; result = 0; do if (*s == c) result = s; while (*s++ != 0); return(result); } setbuf.cС eЯ Є6#include "../include/stdio.h" setbuf(iop, buffer) FILE *iop; char *buffer; { if ( iop->_buf && testflag(iop,IOMYBUF) ) free(iop->_buf); iop->_flags &= ~(IOMYBUF | UNBUFF | PERPRINTF); iop->_buf = buffer; if ( iop->_buf == NULL ) iop->_flags |= UNBUFF; iop->_ptr = iop->_buf; iop->_count = 0; } setgid.cС eЯ Є„#include "../include/lib.h" PUBLIC int setgid(grp) int grp; { return callm1(MM, SETGID, grp, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } setuid.cС fЯ Є„#include "../include/lib.h" PUBLIC int setuid(usr) int usr; { return callm1(MM, SETUID, usr, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } signal.cС fЯ ЄF#include "../include/lib.h" #include "../include/signal.h" int (*vectab[NR_SIGS])(); /* array of functions to catch signals */ /* The definition of signal really should be * PUBLIC int (*signal(signr, func))() * but some compilers refuse to accept this, even though it is correct. * The only thing to do if you are stuck with such a defective compiler is * change it to * PUBLIC int *signal(signr, func) * and change ../h/signal.h accordingly. */ PUBLIC int (*signal(signr, func))() int signr; /* which signal is being set */ int (*func)(); /* pointer to function that catches signal */ { int r,(*old)(); old = vectab[signr - 1]; vectab[signr - 1] = func; M.m6_i1 = signr; M.m6_f1 = ( (func == SIG_IGN || func == SIG_DFL) ? func : begsig); r = callx(MM, SIGNAL); return( (r < 0 ? (int (*)()) r : old) ); } sleep.cС fЯ Є#include "../include/lib.h" #include "../include/signal.h" PRIVATE alfun(){} /* used with sleep() below */ PUBLIC sleep(n) int n; { /* sleep(n) pauses for 'n' seconds by scheduling an alarm interrupt. */ signal(SIGALRM, alfun); alarm(n); pause(); } sprintf.cС fЯ Є1#include "../include/stdio.h" char *sprintf(buf,format,args) char *buf, *format; int args; { FILE _tempfile; _tempfile._fd = -1; _tempfile._flags = WRITEMODE + STRINGS; _tempfile._buf = buf; _tempfile._ptr = buf; _doprintf(&_tempfile,format,&args); putc('\0',&_tempfile); return buf; } (stat.cС fЯ ЄЏ#include "../include/lib.h" PUBLIC int stat(name, buffer) char *name; char *buffer; { int n; n = callm1(FS, STAT, len(name), 0, 0, name, buffer, NIL_PTR); return(n); } 0stb.cС fЯ Єг /* library routine for copying structs with unpleasant alignment */ __stb(n, f, t) register char *f, *t; register int n; { if (n > 0) do *t++ = *f++; while (--n); } Tstderr.cС fЯ ЄTstd_err(s) char *s; { char *p = s; while(*p != 0) p++; write(2, s, p - s); } stime.cС fЯ Єo#include "../include/lib.h" PUBLIC int stime(top) long *top; { M.m2_l1 = *top; return callx(FS, STIME); } *strcat.cС fЯ Є char *strcat(s1, s2) register char *s1, *s2; { /* Append s2 to the end of s1. */ char *original = s1; /* Find the end of s1. */ while (*s1 != 0) s1++; /* Now copy s2 to the end of s1. */ while (*s2 != 0) *s1++ = *s2++; *s1 = 0; return(original); } astrcmp.cС fЯ ЄЇint strcmp(s1, s2) register char *s1, *s2; { /* Compare 2 strings. */ while (1) { if (*s1 != *s2) return(*s1 - *s2); if (*s1 == 0) return(0); s1++; s2++; } } Nstrcpy.cС gЯ ЄŸchar *strcpy(s1, s2) register char *s1, *s2; { /* Copy s2 to s1. */ char *original = s1; while (*s2 != 0) *s1++ = *s2++; *s1 = 0; return(original); } ;strlen.cС gЯ Є|int strlen(s) char *s; { /* Return length of s. */ char *original = s; while (*s != 0) s++; return(s - original); } strncat.cС gЯ Єjchar *strncat(s1, s2, n) register char *s1, *s2; int n; { /* Append s2 to the end of s1, but no more than n characters */ char *original = s1; if (n == 0) return(s1); /* Find the end of s1. */ while (*s1 != 0) s1++; /* Now copy s2 to the end of s1. */ while (*s2 != 0) { *s1++ = *s2++; if (--n == 0) break; } *s1 = 0; return(original); } strncmp.cС gЯ Єкint strncmp(s1, s2, n) register char *s1, *s2; int n; { /* Compare two strings, but at most n characters. */ while (1) { if (*s1 != *s2) return(*s1 - *s2); if (*s1 == 0 || --n == 0) return(0); s1++; s2++; } } strncpy.cС gЯ Єтchar *strncpy(s1, s2, n) register char *s1, *s2; int n; { /* Copy s2 to s1, but at most n characters. */ char *original = s1; while (*s2 != 0) { *s1++ = *s2++; if (--n == 0) break; } *s1 = 0; return(original); } sync.cС gЯ Єr#include "../include/lib.h" PUBLIC int sync() { return callm1(FS, SYNC, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } syslib.cС gЯ Єи #include "../h/const.h" #include "../h/type.h" #include "../h/callnr.h" #include "../h/com.h" #include "../h/error.h" #define FS FS_PROC_NR #define MM MMPROCNR extern int errno; extern message M; /*---------------------------------------------------------------------------- Messages to systask (special calls) ----------------------------------------------------------------------------*/ PUBLIC sys_xit(parent, proc) int parent; /* parent of exiting proc. */ int proc; /* which proc has exited */ { /* A proc has exited. Tell the kernel. */ callm1(SYSTASK, SYS_XIT, parent, proc, 0, NIL_PTR, NIL_PTR, NIL_PTR); } PUBLIC sys_getsp(proc, newsp) int proc; /* which proc has enabled signals */ vir_bytes *newsp; /* place to put sp read from kernel */ { /* Ask the kernel what the sp is. */ callm1(SYSTASK, SYS_GETSP, proc, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); *newsp = (vir_bytes) M.STACK_PTR; } PUBLIC sys_sig(proc, sig, sighandler) int proc; /* which proc has exited */ int sig; /* signal number: 1 - 16 */ int (*sighandler)(); /* pointer to signal handler in user space */ { /* A proc has to be signaled. Tell the kernel. */ M.m6_i1 = proc; M.m6_i2 = sig; M.m6_f1 = sighandler; callx(SYSTASK, SYS_SIG); } PUBLIC sys_fork(parent, child, pid) int parent; /* proc doing the fork */ int child; /* which proc has been created by the fork */ int pid; /* process id assigned by MM */ { /* A proc has forked. Tell the kernel. */ callm1(SYSTASK, SYS_FORK, parent, child, pid, NIL_PTR, NIL_PTR, NIL_PTR); } PUBLIC sys_exec(proc, ptr) int proc; /* proc that did exec */ char *ptr; /* new stack pointer */ { /* A proc has exec'd. Tell the kernel. */ callm1(SYSTASK, SYS_EXEC, proc, 0, 0, ptr, NIL_PTR, NIL_PTR); } PUBLIC sys_newmap(proc, ptr) int proc; /* proc whose map is to be changed */ char *ptr; /* pointer to new map */ { /* A proc has been assigned a new memory map. Tell the kernel. */ callm1(SYSTASK, SYS_NEWMAP, proc, 0, 0, ptr, NIL_PTR, NIL_PTR); } PUBLIC sys_copy(mptr) message *mptr; /* pointer to message */ { /* A proc wants to use local copy. */ /* Make this routine better. Also check other guys' error handling -DEBUG */ mptr->m_type = SYS_COPY; if (sendrec(SYSTASK, mptr) != OK) panic("sys_copy can't send", NO_NUM); } PUBLIC sys_times(proc, ptr) int proc; /* proc whose times are needed */ real_time ptr[4]; /* pointer to time buffer */ { /* Fetch the accounting info for a proc. */ callm1(SYSTASK, SYS_TIMES, proc, 0, 0, ptr, NIL_PTR, NIL_PTR); ptr[0] = M.USER_TIME; ptr[1] = M.SYSTEM_TIME; ptr[2] = M.CHILD_UTIME; ptr[3] = M.CHILD_STIME; } PUBLIC sys_abort() { /* Something awful has happened. Abandon ship. */ callm1(SYSTASK, SYS_ABORT, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } PUBLIC int tell_fs(what, p1, p2, p3) int what, p1, p2, p3; { /* This routine is only used by MM to inform FS of certain events: * tell_fs(CHDIR, slot, dir, 0) * tell_fs(EXIT, proc, 0, 0) * tell_fs(FORK, parent, child, 0) * tell_fs(SETGID, proc, realgid, effgid) * tell_fs(SETUID, proc, realuid, effuid) * tell_fs(SYNC, 0, 0, 0) * tell_fs(UNPAUSE, proc, signr, 0) */ callm1(FS, what, p1, p2, p3, NIL_PTR, NIL_PTR, NIL_PTR); } time.cС hЯ Є #include "../include/lib.h" PUBLIC long time(tp) long *tp; { int k; long l; k = callm1(FS, TIME, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); if (M.m_type < 0 || k != OK) {errno = -M.m_type; return(-1L);} l = M.m2_l1; if (tp != (long *) 0) *tp = l; return(l); } ttimes.cС hЯ Є#include "../include/lib.h" struct tbuf { long b1, b2, b3, b4;}; PUBLIC int times(buf) struct tbuf *buf; { int k; k = callm1(FS, TIMES, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); buf->b1 = M.m4_l1; buf->b2 = M.m4_l2; buf->b3 = M.m4_l3; buf->b4 = M.m4_l4; return(k); } sumask.cС hЯ Є”#include "../include/lib.h" PUBLIC int umask(complmode) int complmode; { return callm1(FS, UMASK, complmode, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); } umount.cС hЯ Єk#include "../include/lib.h" PUBLIC int umount(name) char* name; { return callm3(FS, UMOUNT, 0, name); } oungetc.cС hЯ Є)#include "../include/stdio.h" ungetc(ch, iop) int ch; FILE *iop; { if ( ch < 0 || !testflag(iop,READMODE) || testflag(iop,UNBUFF) ) return( EOF ); if ( iop->_count >= BUFSIZ) return(EOF); if ( iop->_ptr == iop->_buf) iop->_ptr++; iop->_count++; *--iop->_ptr = ch; return(ch); } iunlink.cС hЯ Єk#include "../include/lib.h" PUBLIC int unlink(name) char *name; { return callm3(FS, UNLINK, 0, name); } )utime.cС hЯ ЄЦ#include "../include/lib.h" PUBLIC int utime(name, timp) char *name; long timp[2]; { M.m2_i1 = len(name); M.m2_l1 = timp[0]; M.m2_l2 = timp[1]; M.m2_p1 = name; return callx(FS, UTIME); } wait.cС iЯ Є­#include "../include/lib.h" PUBLIC int wait(status) int *status; { int k; k = callm1(MM, WAIT, 0, 0, 0, NIL_PTR, NIL_PTR, NIL_PTR); *status = M.m2_i1; return(k); } ewrite.cС iЯ ЄЅ#include "../include/lib.h" PUBLIC int write(fd, buffer, nbytes) char *buffer; int nbytes; { return callm1(FS, WRITE, fd, nbytes, 0, buffer, NIL_PTR, NIL_PTR); } brksize.sС [Я Є5.data .globl endbss, _brksize _brksize: .word endbss fcatchsig.sС [Я Є.globl _begsig .globl _vectab, _M mtype = 2 | M+mtype = &M.m_type _begsig: push ax | after interrupt, save all regs push bx push cx push dx push si push di push bp push ds push es mov bx,sp mov bx,18(bx) | bx = signal number mov ax,bx | ax = signal number dec bx | vectab[0] is for sig 1 add bx,bx | pointers are two bytes on 8088 mov bx,_vectab(bx) | bx = address of routine to call push _M+mtype | push status of last system call push ax | func called with signal number as arg call (bx) back: pop ax | get signal number off stack pop _M+mtype | restore status of previous system call pop es | signal handling finished pop ds pop bp pop di pop si pop dx pop cx pop bx pop ax pop dummy | remove signal number from stack iret .data dummy: .word 0 csv.sС \Я ЄйWRITE = 4 FS = 1 |*===========================================================================* |* csv & cret * |*===========================================================================* | This version of csv replaces the standard one. It checks sp. .globl csv, cret, _sp_limit, _sendrec, _M csv: pop bx | bx = return address push bp | stack old frame pointer mov bp,sp | set new frame pointer to sp push di | save di push si | save si sub sp,ax | ax = # bytes of local variables cmp sp,_sp_limit | has stack overflowed? jb csv.err | jump if stack overflow jmp (bx) | normal return: copy bx to pc csv.err: | come here if stack overflow mov _M+2,#WRITE | m_type mov _M+4,#2 | file descriptor 2 is std error mov _M+6,#15 | prepare to print error message mov _M+10,#stkovmsg | error message mov ax,#_M | prepare to call sendrec(FS, &M); push ax | push second parameter mov ax,#FS | prepare to push first parameter push ax | push first parameter call _sendrec | write(fd, stkovmsg, 15); add sp,#4 | clean up stack L0: jmp L0 | hang forever cret: lea sp,*-4(bp) pop si pop di pop bp ret .data _sp_limit: .word 0 | stack limit default is 0 stkovmsg: .asciz "Stack overflow\n" rgetutil.sС `Я Єш.globl _get_base, _get_size, _get_tot_mem .globl endbss |*========================================================================* | utilities * |*========================================================================* _get_base: | return click at which prog starts mov ax,ds ret _get_size: | return prog size in bytes (text+data+bss) mov ax,#endbss | end is compiler label at end of bss ret | Find out how much memory the machine has, including vectors, kernel MM, etc. _get_tot_mem: cli push es push di mov ax,#8192 | start search at 128K (8192 clicks) sub di,di L1: mov es,ax seg es mov (di),#0xA5A4 | write random bit pattern to memory xor bx,bx seg es mov bx,(di) | read back pattern just written cmp bx,#0xA5A4 | compare with expected value jne L2 | if different, no memory present add ax,#4096 | advance counter by 64K cmp ax,#0xA000 | stop seaching at 640K jne L1 L2: pop di pop es sti ret sendrec.sС eЯ Є| See ../h/com.h for C definitions SEND = 1 RECEIVE = 2 BOTH = 3 SYSVEC = 32 |*========================================================================* | send and receive * |*========================================================================* | send(), receive(), sendrec() all save bp, but destroy ax, bx, and cx. .globl _send, _receive, _sendrec _send: mov cx,*SEND | send(dest, ptr) jmp L0 _receive: mov cx,*RECEIVE | receive(src, ptr) jmp L0 _sendrec: mov cx,*BOTH | sendrec(srcdest, ptr) jmp L0 L0: push bp | save bp mov bp,sp | can't index off sp mov ax,4(bp) | ax = dest-src mov bx,6(bp) | bx = message pointer int SYSVEC | trap to the kernel pop bp | restore bp ret | return crtso.sС \Я ЄV| This is the C run-time start-off routine. It's job is to take the | arguments as put on the stack by EXEC, and to parse them and set them up the | way _main expects them. .globl _main, _exit, crtso, _environ .globl begtext, begdata, begbss, endtext, enddata, endbss .text begtext: crtso: mov bx,sp mov cx,(bx) add bx,*2 mov ax,cx inc ax shl ax,#1 add ax,bx mov _environ,ax | save envp in environ push ax | push environ push bx | push argv push cx | push argc call _main add sp,*6 push ax | push exit status call _exit .data begdata: _environ: .word 0 .bss begbss: end.sС ]Я Єg.globl endtext, enddata, endbss, _end, _edata .text endtext: .data enddata: _edata: .bss endbss: _end: head.sС aЯ Єо.globl _main, _stackpt, begtext, begdata, begbss, _data_org, _exit .text begtext: jmp L0 .zerow 7 | kernel uses this area as stack for inital IRET L0: mov sp,_stackpt call _main L1: jmp L1 | this will never be executed _exit: jmp _exit | this will never be executed either .data begdata: _data_org: | fs needs to know where build stuffed table .word 0xDADA,0,0,0,0,0,0,0 | first 8 words of MM, FS, INIT are for stack | 0xDADA is magic number for build .bss begbss: setjmp.sС fЯ ЄУ.globl _setjmp, _longjmp .globl csv .text _setjmp: mov bx,sp mov ax,(bx) mov bx,*2(bx) mov (bx),bp mov *2(bx),sp mov *4(bx),ax xor ax,ax ret _longjmp: xor ax,ax call csv mov bx,*4(bp) mov ax,*6(bp) or ax,ax jne L1 inc ax L1: mov cx,(bx) L2: cmp cx,*0(bp) je L3 mov bp,*0(bp) or bp,bp jne L2 hlt L3: mov di,*-2(bp) mov si,*-4(bp) mov bp,*0(bp) mov sp,*2(bx) mov cx,*4(bx) mov bx,sp mov (bx),cx ret bscanf.cС dЯ Єэ/* scanf - formatted input conversion Author: Patrick van Kleef */ #include "stdio.h" int scanf (format, args) char *format; unsigned args; { return _doscanf (0, stdin, format, &args); } int fscanf (fp, format, args) FILE *fp; char *format; unsigned args; { return _doscanf (0, fp, format, &args); } int sscanf (string, format, args) char *string; /* source of data */ char *format; /* control string */ unsigned args; /* our args */ { return _doscanf (1, string, format, &args); } union ptr_union { char *chr_p; unsigned int *uint_p; unsigned long *ulong_p; }; static int ic; /* the current character */ static char *rnc_arg; /* the string or the filepointer */ static rnc_code; /* 1 = read from string, else from FILE */ /* get the next character */ static rnc () { if (rnc_code) { if (!(ic = *rnc_arg++)) ic = EOF; } else ic = getc ((FILE *) rnc_arg); } /* * unget the current character */ static ugc () { if (rnc_code) --rnc_arg; else ungetc (ic, rnc_arg); } static scnindex(ch, string) char ch; char *string; { while (*string++ != ch) if (!*string) return 0; return 1; } /* * this is cheaper than iswhite from */ static iswhite (ch) int ch; { return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); } static isdigit (ch) int ch; { return (ch >= '0' && ch <= '9'); } static tolower (ch) int ch; { if (ch >= 'A' && ch <= 'Z') ch = ch + 'a' - 'A'; return ch; } /* * the routine that does the job */ _doscanf (code, funcarg, format, argp) int code; /* function to get a character */ char *funcarg; /* an argument for the function */ char *format; /* the format control string */ union ptr_union *argp; /* our argument list */ { int done = 0; /* number of items done */ int base; /* conversion base */ long val; /* an integer value */ int sign; /* sign flag */ int do_assign; /* assignment suppression flag */ unsigned width; /* width of field */ int widflag; /* width was specified */ int longflag; /* true if long */ int done_some; /* true if we have seen some data */ int reverse; /* reverse the checking in [...] */ char *endbracket; /* position of the ] in format string */ rnc_arg = funcarg; rnc_code = code; rnc (); /* read the next character */ if (ic == EOF) { done = EOF; goto quit; } while (1) { while (iswhite (*format)) ++format; /* skip whitespace */ if (!*format) goto all_done; /* end of format */ if (ic < 0) goto quit; /* seen an error */ if (*format != '%') { while (iswhite (ic)) rnc (); if (ic != *format) goto all_done; ++format; rnc (); ++done; continue; } ++format; do_assign = 1; if (*format == '*') { ++format; do_assign = 0; } if (isdigit (*format)) { widflag = 1; for (width = 0; isdigit (*format);) width = width * 10 + *format++ - '0'; } else widflag = 0; /* no width spec */ if (longflag = (tolower (*format) == 'l')) ++format; if (*format != 'c') while (iswhite (ic)) rnc (); done_some = 0; /* nothing yet */ switch (*format) { case 'o': base = 8; goto decimal; case 'u': case 'd': base = 10; goto decimal; case 'x': base = 16; if (((!widflag) || width >= 2) && ic == '0') { rnc (); if (tolower (ic) == 'x') { width -= 2; done_some = 1; rnc (); } else { ugc (); ic = '0'; } } decimal: val = 0L; /* our result value */ sign = 0; /* assume positive */ if (!widflag) width = 0xffff; /* very wide */ if (width && ic == '+') rnc (); else if (width && ic == '-') { sign = 1; rnc (); } while (width--) { if (isdigit (ic) && ic - '0' < base) ic -= '0'; else if (base == 16 && tolower (ic) >= 'a' && tolower (ic) <= 'f') ic = 10 + tolower (ic) - 'a'; else break; val = val * base + ic; rnc (); done_some = 1; } if (do_assign) { if (sign) val = -val; if (longflag) *(argp++)->ulong_p = (unsigned long) val; else *(argp++)->uint_p = (unsigned) val; } if (done_some) ++done; else goto all_done; break; case 'c': if (!widflag) width = 1; while (width-- && ic >= 0) { if (do_assign) *(argp)->chr_p++ = (char) ic; rnc (); done_some = 1; } if (do_assign) argp++; /* done with this one */ if (done_some) ++done; break; case 's': if (!widflag) width = 0xffff; while (width-- && !iswhite (ic) && ic > 0) { if (do_assign) *(argp)->chr_p++ = (char) ic; rnc (); done_some = 1; } if (do_assign) /* terminate the string */ *(argp++)->chr_p = '\0'; if (done_some) ++done; else goto all_done; break; case '[': if (!widflag) width = 0xffff; if ( *(++format) == '^' ) { reverse = 1; format++; } else reverse = 0; endbracket = format; while ( *endbracket != ']' && *endbracket != '\0') endbracket++; if (!*endbracket) goto quit; *endbracket = '\0'; /* change format string */ while (width-- && !iswhite (ic) && ic > 0 && (scnindex (ic, format) ^ reverse)) { if (do_assign) *(argp)->chr_p++ = (char) ic; rnc (); done_some = 1; } format = endbracket; *format = ']'; /* put it back */ if (do_assign) /* terminate the string */ *(argp++)->chr_p = '\0'; if (done_some) ++done; else goto all_done; break; } /* end switch */ ++format; } all_done: if (ic >= 0) ugc (); /* restore the character */ quit: return done; } nctime.cС Дж Є-struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; struct timeb { long time; unsigned short millitm; short timezone; short dstflag; }; extern struct tm *localtime(); extern char *asctime(); char *ctime(clock) long *clock; { return asctime(localtime(clock)); } static int monthsize[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; #define SECS_DAY (24*60L*60L) #define YEARSIZE(year) ((year) % 4 ? 365 : 366) struct tm * gmtime(clock) long *clock; { long cl = *clock; long dayclock, dayno; static struct tm tm_buf; register struct tm *pbuf = &tm_buf; register int *months = monthsize; int year = 1970; dayclock = cl % SECS_DAY; dayno = cl / SECS_DAY; pbuf->tm_sec = dayclock % 60; pbuf->tm_min = (dayclock % 3600) / 60; pbuf->tm_hour = dayclock / 3600; pbuf->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */ while (dayno >= YEARSIZE(year)) { dayno -= YEARSIZE(year); year++; } pbuf->tm_year = year - 1900; pbuf->tm_yday = dayno; pbuf->tm_isdst = 0; if (YEARSIZE(year) == 366) monthsize[1] = 29; while (dayno - *months >= 0) dayno -= *months++; pbuf->tm_mday = dayno + 1; pbuf->tm_mon = months - monthsize; monthsize[1] = 28; return pbuf; } #define FIRSTSUNDAY(t) (((t)->tm_yday - (t)->tm_wday + 420) % 7) #define SUNDAY(day, t) ((day) < 58 ? \ ((day) < FIRSTSUNDAY(t) ? FIRSTSUNDAY(t) : static int last_sunday(d, t) register int d; register struct tm *t; { int first = FIRSTSUNDAY(t); if (d >= 58 && YEARSIZE(t->tm_year)) d++; if (d < first) return first; return d - (d - first) % 7; } extern struct tm *gmtime(); struct tm * localtime(clock) long *clock; { register struct tm *gmt; long cl; int begindst, enddst; extern int __daylight; extern long __timezone; tzset(); cl = *clock - __timezone; gmt = gmtime(&cl); if (__daylight) { /* daylight saving time. Unfortunately, rules differ for different countries. Implemented here are heuristics that got it right in Holland, over the last couple of years. Of course, there is no algorithm. It is all politics ... */ begindst = last_sunday(89, gmt); /* last Sun before Apr */ enddst = last_sunday(272, gmt); /* last Sun in Sep */ if ((gmt->tm_yday>begindst || (gmt->tm_yday==begindst && gmt->tm_hour>=2)) && (gmt->tm_ydaytm_yday==enddst && gmt->tm_hour<3))) { /* it all happens between 2 and 3 */ cl += 1*60*60; gmt = gmtime(&cl); gmt->tm_isdst++; } } return gmt; } #ifdef BSD4_2 #else #ifndef USG #endif #endif #ifdef USG long timezone = -1 * 60; int daylight = 1; char *tzname[] = {"MET", "MDT",}; #endif long __timezone = -1 * 60; int __daylight = 1; char *__tzname[] = {"MET", "MDT", }; tzset() { #ifdef BSD4_2 struct timeval tval; struct timezone tzon; gettimeofday(&tval, &tzon); __timezone = tzon.tz_minuteswest * 60L; __daylight = tzon.tz_dsttime; #else #ifndef USG struct timeb time; ftime(&time); __timezone = time.timezone*60L; __daylight = time.dstflag; #endif #endif { extern char *getenv(); register char *p = getenv("TZ"); if (p && *p) { register int n = 0; int sign = 1; strncpy(__tzname[0], p, 3); p += 3; if (*(p += 3) == '-') { sign = -1; p++; } while(*p >= '0' && *p <= '9') n = 10 * n + (*p++ - '0'); n *= sign; __timezone = ((long)(n * 60)) * 60; __daylight = (*p != '\0'); strncpy(__tzname[1], p, 3); } } #ifdef USG timezone = __timezone; daylight = __daylight; tzname[0] = __tzname[0]; tzname[1] = __tzname[1]; #endif } ipopen.cС cЯ Є#include #include static int pids[20]; FILE * popen(command, type) char *command, *type; { int piped[2]; int Xtype = *type == 'r' ? 0 : *type == 'w' ? 1 : 2; int pid; if (Xtype == 2 || pipe(piped) < 0 || (pid = fork()) < 0) return 0; if (pid == 0) { /* child */ register int *p; for (p = pids; p < &pids[20]; p++) { if (*p) close(p - pids); } close(piped[Xtype]); dup2(piped[!Xtype], !Xtype); close(piped[!Xtype]); execl("/bin/sh", "sh", "-c", command, (char *) 0); _exit(127); /* like system() ??? */ } pids[piped[Xtype]] = pid; close(piped[!Xtype]); return fdopen(piped[Xtype], type); } pclose(iop) FILE *iop; { int fd = fileno(iop); int status, wret; int (*intsave)() = signal(SIGINT, SIG_IGN); int (*quitsave)() = signal(SIGQUIT, SIG_IGN); fclose(iop); while ((wret = wait(&status)) != -1) { if (wret == pids[fd]) break; } if (wret == -1) status = -1; signal(SIGINT, intsave); signal(SIGQUIT, quitsave); pids[fd] = 0; return status; } ,qsort.cС dЯ Є static qsort1(); static int (*qcompar)(); static qexchange(); static q3exchange(); qsort(base, nel, width, compar) char *base; int (*compar)(); { qcompar = compar; qsort1(base, base + (nel - 1) * width, width); } static qsort1(a1, a2, width) char *a1, *a2; register int width; { register char *left, *right; register char *lefteq, *righteq; int cmp; for (;;) { if (a2 <= a1) return; left = a1; right = a2; lefteq = righteq = a1 + width * (((a2-a1)+width)/(2*width)); /* Pick an element in the middle of the array. We will collect the equals around it. "lefteq" and "righteq" indicate the left and right bounds of the equals respectively. Smaller elements end up left of it, larger elements end up right of it. */ again: while (left < lefteq && (cmp = (*qcompar)(left, lefteq)) <= 0) { if (cmp < 0) { /* leave it where it is */ left += width; } else { /* equal, so exchange with the element to the left of the "equal"-interval. */ lefteq -= width; qexchange(left, lefteq, width); } } while (right > righteq) { if ((cmp = (*qcompar)(right, righteq)) < 0) { /* smaller, should go to left part */ if (left < lefteq) { /* yes, we had a larger one at the left, so we can just exchange */ qexchange(left, right, width); left += width; right -= width; goto again; } /* no more room at the left part, so we move the "equal-interval" one place to the right, and the smaller element to the left of it. This is best expressed as a three-way exchange. */ righteq += width; q3exchange(left, righteq, right, width); lefteq += width; left = lefteq; } else if (cmp == 0) { /* equal, so exchange with the element to the right of the "equal-interval" */ righteq += width; qexchange(right, righteq, width); } else /* just leave it */ right -= width; } if (left < lefteq) { /* larger element to the left, but no more room, so move the "equal-interval" one place to the left, and the larger element to the right of it. */ lefteq -= width; q3exchange(right, lefteq, left, width); righteq -= width; right = righteq; goto again; } /* now sort the "smaller" part */ qsort1(a1, lefteq - width, width); /* and now the larger, saving a subroutine call because of the for(;;) */ a1 = righteq + width; } /*NOTREACHED*/ } static qexchange(p, q, n) register char *p, *q; register int n; { register int c; while (n-- > 0) { c = *p; *p++ = *q; *q++ = c; } } static q3exchange(p, q, r, n) register char *p, *q, *r; register int n; { register int c; while (n-- > 0) { c = *p; *p++ = *r; *r++ = *q; *q++ = c; } } system.c !f Є‚#include #ifndef NIL #define NIL 0 #endif system( cmd ) char *cmd; { int retstat, procid, waitstat; if( (procid = fork()) == 0 ) { execl( "/bin/sh", "sh", "-c", cmd, NIL ); exit( 127 ); } while( (waitstat = wait(&retstat)) != procid && waitstat != -1 ) ; if (waitstat == -1) retstat = -1; return( retstat ); } puts.cх Nй Єu#include "stdio.h" int puts(s) char *s; { char c; c = fputs(s,stdout); putchar('\n'); return(c); } -.)...crtso.s/end.s0head.s1setjmp.s| This is the C run-time start-off routine. It's job is to take the | arguments as put on the stack by EXEC, and to parse them and set them up the | way _main expects them. .globl _main, _exit, crtso, _environ .globl begtext, begdata, begbss, endtext, enddata, endbss .text begtext: crtso: mov bx,sp mov cx,(bx) add bx,*2 mov ax,cx inc ax shl ax,#1 add ax,bx mov _environ,ax | save envp in environ push ax | push environ push bx | push argv push cx | push argc call _main add sp,*6 push ax | push exit status call _exit .data begdata: _environ: .word 0 .bss begbss: .globl endtext, enddata, endbss, _end, _edata .text endtext: .data enddata: _edata: .bss endbss: _end: .globl _main, _stackpt, begtext, begdata, begbss, _data_org, _exit .text begtext: jmp L0 .zerow 7 | kernel uses this area as stack for inital IRET L0: mov sp,_stackpt call _main L1: jmp L1 | this will never be executed _exit: jmp _exit | this will never be executed either .data begdata: _data_org: | fs needs to know where build stuffed table .word 0xDADA,0,0,0,0,0,0,0 | first 16 words of MM, FS, INIT are for stack | 0xDADA is magic number for build .bss begbss: .globl _setjmp, _longjmp .text _setjmp: mov bx,sp mov ax,(bx) mov bx,*2(bx) mov (bx),bp mov *2(bx),sp mov *4(bx),ax xor ax,ax ret _longjmp: xor ax,ax push bp mov bp,sp mov bx,*4(bp) mov ax,*6(bp) or ax,ax jne L1 inc ax L1: mov cx,(bx) L2: cmp cx,*0(bp) je L3 mov bp,*0(bp) or bp,bp jne L2 hlt L3: mov bp,*0(bp) mov sp,*2(bx) mov cx,*4(bx) mov bx,sp mov (bx),cx ret 2.)..3crtso.s4end.s5head.s6setjmp.s| This is the C run-time start-off routine. It's job is to take the | arguments as put on the stack by EXEC, and to parse them and set them up the | way _main expects them. .globl _main, _exit, crtso, _environ .globl begtext, begdata, begbss, endtext, enddata, endbss .text begtext: crtso: mov bx,sp mov cx,(bx) add bx,*2 mov ax,cx inc ax shl ax,#1 add ax,bx mov _environ,ax | save envp in environ push ax | push environ push bx | push argv push cx | push argc call _main add sp,*6 push ax | push exit status call _exit .data begdata: _environ: .word 0 .bss begbss: .globl endtext, enddata, endbss, _end, _edata .text endtext: .data enddata: _edata: .bss endbss: _end: .globl _main, _stackpt, begtext, begdata, begbss, _data_org, _exit .text begtext: jmp L0 .zerow 7 | kernel uses this area as stack for inital IRET L0: mov sp,_stackpt call _main L1: jmp L1 | this will never be executed _exit: jmp _exit | this will never be executed either .data begdata: _data_org: | fs needs to know where build stuffed table .word 0xDADA,0,0,0,0,0,0,0 | first 8 words of MM, FS, INIT are for stack | 0xDADA is magic number for build .bss begbss: .globl _setjmp, _longjmp .globl csv .text _setjmp: mov bx,sp mov ax,(bx) mov bx,*2(bx) mov (bx),bp mov *2(bx),sp mov *4(bx),ax xor ax,ax ret _longjmp: xor ax,ax call csv mov bx,*4(bp) mov ax,*6(bp) or ax,ax jne L1 inc ax L1: mov cx,(bx) L2: cmp cx,*0(bp) je L3 mov bp,*0(bp) or bp,bp jne L2 hlt L3: mov di,*-2(bp) mov si,*-4(bp) mov bp,*0(bp) mov sp,*2(bx) mov cx,*4(bx) mov bx,sp mov (bx),cx ret 7.)..8brksize.asm9catchsig.asm:crtso.asm;csv.asm<end.asm=getutil.asm>head.asm?prologue.h@sendrec.asmAsetjmp.asmBmake.batTitle brksize - dynamic end of bss ; Note: as opposed to the symbol 'end' which statically sets the end ; of the unitialized (allocated) data, brksize can set this ; dynamically. It is used by the memory-allocator to allocate ; free memory on top of the bss. Thus stack and heap grow ; towards eachother. ; ; This symbol is (must be) declared in crtso.asm which defines ; the load-model for C86-programs under Minix. END title catchsig - C86 version page,132 INCLUDE ..\lib\prologue.h PUBLIC begsig EXTRN vectab:WORD, M:WORD @CODE SEGMENT ASSUME CS:@CODE,DS:DGROUP MTYPE = 2 ; M+mtype = &M.m_type begsig: push ax ; after interrupt, save all regs push bx push cx push dx push si push di push bp push ds push es mov bp,sp mov bx,18[bp] ; bx = signal number dec bx ; vectab[0] is for sig 1 add bx,bx ; pointers are two bytes on 8088 mov bx,vectab[bx] ; bx = address of routine to call push M+mtype ; push status of last system call push ax ; func called with signal number as arg call bx back: pop ax ; get signal number off stack pop M+mtype ; restore status of previous system call pop es ; signal handling finished pop ds pop bp pop di pop si pop dx pop cx pop bx pop ax pop dummy ; remove signal number from stack iret @CODE ENDS @DATAI SEGMENT dummy DW 0 @DATAI ENDS END ; end of assembly-file Title $MAIN C-86 run-time start-off for Minix page,132 PUBLIC @end, brksize, edata, $main, environ, kamikaze EXTRN main:NEAR, exit:NEAR INCLUDE ..\lib\prologue.h STACKSIZE EQU 2560 ; default stack is 5 Kb (2K words) @CODE SEGMENT ASSUME CS:@CODE,DS:DGROUP ; This is the C run-time start-off routine. It's job is to take the ; arguments as put on the stack by EXEC, and to parse them and set them up the ; way main expects them. ; $main: mov ax,DGROUP ; force relocation of data & bss mov bx,sp ; set stackframe pointer (ds=ss) mov cx,[bx] ; get argc add bx,2 ; point at next parameter mov ax,cx inc ax ; calculate envp shl ax,1 add ax,bx mov environ,ax ; save envp push ax ; stack envp push bx ; stack argv push cx ; stack argc call main ; call main (arc,argv,envp) add sp,6 push ax ; stack program-termination status call exit ; this will never return ; DEBUG from here on kamikaze: int 3 ret @CODE ENDS @DATAC SEGMENT brksize DW offset dgroup:@END+2 ; dynamic changeable end of bss environ DW 0 ; save environment pointer here @DATAC ENDS @DATAT SEGMENT ; DATAT holds nothing. The label just edata label byte ; tells us where the initialized data @DATAT ENDS ; (.data) ends. @DATAV SEGMENT ; DATAV holds nothing. The label in @end label byte ; the segment just tells us where @DATAV ENDS ; the data+bss ends. @STACK SEGMENT BYTE STACK 'STACK' DW STACKSIZE dup(?) ; add stack segment to bss @STACK ENDS END $main ; program entry-point (could be anywhere) Title csv - C86 version ; Note: csv is a symbol generated by the Minix and PC-IX C-compilers. ; It is used to do procedure entry/exit. The .s version checks ; the stack. In C86 no such symbol is generated by the compiler ; so we can't use these routines. ; ; If you want to do stack-checking, you can use the procedure ; '$entry'. This symbol is generated by C86 when you specify ; the '-t' flag when compiling. This routine is primairily ment ; for stack-tracing etc. You must adapt csv then for C86 and call ; it $entry. $entry is called after push bp and allocation for ; local storage (sub sp,nn) END Title end - end of bss for C86 ; Note: This static symbol is the address of the end of the unitialized data ; In C86 this symbol is (and must be) declarfed in crtso.asm, which ; defines the load-model of a program. Its name there is '@END' because ; 'END' is a reserved assembler symbol. C86 knows that these symbols ; must be prepended by the @-sign (so in a C-program you must just ; csall it 'end'). END title GetUtil - C86 version page,132 INCLUDE ..\lib\prologue.h PUBLIC get_base, get_size, get_tot_ EXTRN brksize:WORD, @end:byte @CODE SEGMENT ASSUME CS:@CODE,DS:DGROUP ;======================================================================== ; utilities ;======================================================================== get_base: ; return click at which prog starts mov ax,ds ret get_size: ; return prog size in bytes [text+data+bss] mov ax,offset dgroup:@end ; end is label at end of bss ret ; Find out how much memory the machine has, including vectors, kernel MM, etc. get_tot_: cli push es push di mov ax,8192 ; start search at 128K [8192 clicks] sub di,di y1: mov es,ax mov es:[di],0A5A4h ; write random bit pattern to memory xor bx,bx mov bx,es:[di] ; read back pattern just written cmp bx,0A5A4h ; compare with expected value jne y2 ; if different, no memory present add ax,4096 ; advance counter by 64K cmp ax,0A000h ; stop seaching at 640K jne y1 y2: pop di pop es sti ret @CODE ENDS END ; end of assembly-file title Head - Start-off for initt, mm and fs (C86-compiler) page ,132 EXTRN main:NEAR PUBLIC $main, data_org, brksize, sp_limit, @end EXTRN stackpt:word INCLUDE ..\lib\prologue.h @CODE SEGMENT assume cs:@code,ds:dgroup $main: jmp short L0 ORG 10h ; kernel uses this area as stack for inital IRET L0:mov sp,dgroup:stackpt call main L1:jmp L1 ; this will never be executed mov ax,DGROUP ; force relocation for dos2out (never executed) @CODE ENDS @DATAB SEGMENT ; fs needs to know where build stuffed table data_org DW 0DADAh ; 0xDADA is magic number for build DW 7 dup(0) ; first 8 words of MM, FS, INIT are for stack brksize DW offset dgroup:@END ; first free memory sp_limit DW 0 @DATAB ENDS @DATAV SEGMENT ; DATAV holds nothing. The label in @END label byte ; the segment just tells us where @DATAV ENDS ; the data+bss ends. @STACK SEGMENT BYTE STACK 'STACK' @STACK ENDS ; Add stack to satisfy DOS-linker END $main ; end of assembly & entry-point ; prologue.h ; standard prologue for c86 assembly code ; This file defines the correct ordering of segments @CODE SEGMENT BYTE PUBLIC 'CODE' ; text segment @CODE ENDS @DATAB SEGMENT PARA PUBLIC 'DATAB' ; data segment @DATAB ENDS @DATAC SEGMENT BYTE PUBLIC 'DATAC' ; data segment @DATAC ENDS @DATAI SEGMENT BYTE PUBLIC 'DATAI' ; data segment @DATAI ENDS @DATAT SEGMENT BYTE PUBLIC 'DATAT' ; bss segment @DATAT ENDS @DATAU SEGMENT BYTE PUBLIC 'DATAU' ; bss segment @DATAU ENDS @DATAV SEGMENT BYTE PUBLIC 'DATAV' ; bss segment @DATAV ENDS DGROUP GROUP @DATAB,@DATAC,@DATAI,@DATAT,@DATAU,@DATAV title Send/Receive - C86 version page,132 INCLUDE ..\lib\prologue.h PUBLIC send, receive, send_rec, sendrec ; The following definitions are from ../h/com.h _SEND = 1 _RECEIVE= 2 _BOTH = 3 _SYSVEC = 32 @CODE SEGMENT ASSUME CS:@CODE,DS:DGROUP ;======================================================================== ; send and receive ;======================================================================== ; send(), receive(), send_rec() all save bp, but destroy ax, bx, and cx. send: mov cx,_SEND ; send(dest, ptr) jmp L0 receive: mov cx,_RECEIVE ; receive(src, ptr) jmp L0 sendrec: send_rec: mov cx,_BOTH ; send_rec(srcdest, ptr) jmp L0 L0: push bp ; save bp mov bp,sp ; can't index off sp mov ax,4[bp] ; ax = dest-src mov bx,6[bp] ; bx = message pointer int _SYSVEC ; trap to the kernel pop bp ; restore bp ret ; return @CODE ENDS END ; end of assembly-file title setjmp/longjmp - C86 version page,132 INCLUDE ..\lib\prologue.h PUBLIC setjmp, longjmp EXTRN exit:near ; struct jmpbuf { ; int bp; ; int sp; ; int ret-addr ; } JMPERR EQU -99 ; call exit(JMPERR) when jmp-error @CODE SEGMENT ASSUME CS:@CODE,DS:DGROUP setjmp: mov bx,sp mov ax,[bx] ; ret-addr. mov bx,2[bx] ; addr of jmp-struct mov [bx],bp mov 2[bx],sp mov 4[bx],ax xor ax,ax ret longjmp: mov bp,sp ; set new frame pointer to sp mov bx,4[bp] ; get address of jmp-structure mov ax,6[bp] ; get ret-code or ax,ax jne L1 inc ax ; return code may not be zero L1: mov sp,[bx+2] mov bp,[bx] or bp,bp ; test if last frame-pointer (error) jne L2 ; else execute the longjmp mov ax,JMPERR push ax call exit ; should never return hlt L2: mov bx,[bx+4] jmp [bx] @CODE ENDS END echo off :begin if "%1"=="" goto eind echo/ echo Compiling %1 cc1 %1 > %1.lst if errorlevel 1 goto err cc2 %1 >> %1.lst if errorlevel 1 goto err cc3 %1 >> %1.lst if errorlevel 1 goto err cc4 %1 >> %1.lst if errorlevel 1 goto err shift goto begin :err echo +++error in compilation+++ shift goto begin :eind if '%BATCH%'=='LIB' makelib 09:00 head.s -rw-r--r-- 1 bin 451 Sep 1 09:00 setjmp.s E2џd--75511