inoptr newfile(inoptr pino, char *name) { inoptr nindex; uint8_t j; /* First see if parent is writeable */ if(!(getperm(pino) & OTH_WR)) goto nogood; if(!(nindex = i_open(pino->c_dev, 0))) goto nogood; /* BUG FIX: user/group setting was missing SN */ nindex->c_node.i_uid = udata.u_euid; nindex->c_node.i_gid = udata.u_egid; nindex->c_node.i_mode = F_REG; /* For the time being */ nindex->c_node.i_nlink = 1; nindex->c_node.i_size = 0; for(j=0; j <20; j++) nindex->c_node.i_addr[j] = 0; wr_inode(nindex); if(!ch_link(pino, "", name, nindex)) { i_deref(nindex); goto nogood; } i_deref(pino); return nindex; nogood: i_deref(pino); return NULLINODE; }
arg_t _ioctl(void) { inoptr ino; uint16_t dev; if ((ino = getinode(fd)) == NULLINODE) return -1; if (!(isdevice(ino))) { udata.u_error = ENOTTY; return -1; } if ((request & IOCTL_SUPER) && esuper()) return -1; if (!(getperm(ino) & OTH_WR)) { udata.u_error = EPERM; return -1; } dev = ino->c_node.i_addr[0]; /* top bit of request is reserved for kernel originated magic */ return d_ioctl(dev, request & 0x7FFF, data); }
t_file fillinfo(struct stat stat, char *name) { t_file info; info.name = ft_strdup(name); info.perm = getperm(stat.st_mode); info.nblink = stat.st_nlink; info.owner = ufid(stat.st_uid); info.group = gfid(stat.st_gid); if (info.perm[0] == 'c' || info.perm[0] == 'b') { info.size = pad(3, major(stat.st_rdev), minor(stat.st_rdev)); } else info.size = ft_itoa(stat.st_size); info.time = get_time(&stat); info.modif = stat.st_mtime; info.block = stat.st_blocks; return (info); }
arg_t _utime(void) { inoptr ino; time_t t[2]; if (!(ino = n_open(file, NULLINOPTR))) return (-1); if (ino->c_flags & CRDONLY) { udata.u_error = EROFS; goto out2; } /* Special case in the Unix API - NULL means now */ if (buf) { if (ino->c_node.i_uid != udata.u_euid && esuper()) goto out; if (!valaddr(buf, 2 * sizeof(time_t))) goto out2; uget(buf, t, 2 * sizeof(time_t)); } else { if (!(getperm(ino) & OTH_WR)) goto out; rdtime(&t[0]); memcpy(&t[1], &t[0], sizeof(t[1])); } /* FIXME: needs updating once we pack top bits elsewhere in the inode */ ino->c_node.i_atime = t[0].low; ino->c_node.i_mtime = t[1].low; setftime(ino, C_TIME); i_deref(ino); return (0); out: udata.u_error = EPERM; out2: i_deref(ino); return -1; }
arg_t _execve(void) { /* We aren't re-entrant where this matters */ uint8_t hdr[16]; staticfast inoptr ino; char **nargv; /* In user space */ char **nenvp; /* In user space */ struct s_argblk *abuf, *ebuf; int argc; uint16_t progptr; uint16_t progload; staticfast uint16_t top; uint16_t bin_size; /* Will need to be bigger on some cpus */ uint16_t bss; top = ramtop; if (!(ino = n_open_lock(name, NULLINOPTR))) return (-1); if (!((getperm(ino) & OTH_EX) && (ino->c_node.i_mode & F_REG) && (ino->c_node.i_mode & (OWN_EX | OTH_EX | GRP_EX)))) { udata.u_error = EACCES; goto nogood; } setftime(ino, A_TIME); udata.u_offset = 0; udata.u_count = 16; udata.u_base = hdr; udata.u_sysio = true; readi(ino, 0); if (udata.u_done != 16) { udata.u_error = ENOEXEC; goto nogood; } if (!header_ok(hdr)) { udata.u_error = ENOEXEC; goto nogood2; } progload = hdr[7] << 8; if (progload == 0) progload = PROGLOAD; top = *(uint16_t *)(hdr + 8); if (top == 0) /* Legacy 'all space' binary */ top = ramtop; else /* Requested an amount, so adjust for the base */ top += progload; bss = *(uint16_t *)(hdr + 14); /* Binary doesn't fit */ /* FIXME: review overflows */ bin_size = ino->c_node.i_size; progptr = bin_size + 1024 + bss; if (progload < PROGLOAD || top - progload < progptr || progptr < bin_size) { udata.u_error = ENOMEM; goto nogood2; } udata.u_ptab->p_status = P_NOSLEEP; /* If we made pagemap_realloc keep hold of some defined area we could in theory just move the arguments up or down as part of the process - that would save us all this hassle but replace it with new hassle */ /* Gather the arguments, and put them in temporary buffers. */ abuf = (struct s_argblk *) tmpbuf(); /* Put environment in another buffer. */ ebuf = (struct s_argblk *) tmpbuf(); /* Read args and environment from process memory */ if (rargs(argv, abuf) || rargs(envp, ebuf)) goto nogood3; /* SN */ /* This must be the last test as it makes changes if it works */ /* FIXME: once we sort out chmem we can make stack and data two elements. We never allocate 'code' as there is no split I/D */ /* This is only safe from deadlocks providing pagemap_realloc doesn't sleep */ if (pagemap_realloc(0, top - MAPBASE, 0)) goto nogood3; /* From this point on we are commmited to the exec() completing */ /* Core dump and ptrace permission logic */ #ifdef CONFIG_LEVEL_2 /* Q: should uid == 0 mean we always allow core */ if ((!(getperm(ino) & OTH_RD)) || (ino->c_node.i_mode & (SET_UID | SET_GID))) udata.u_flags |= U_FLAG_NOCORE; else udata.u_flags &= ~U_FLAG_NOCORE; #endif udata.u_top = top; udata.u_ptab->p_top = top; /* setuid, setgid if executable requires it */ if (ino->c_node.i_mode & SET_UID) udata.u_euid = ino->c_node.i_uid; if (ino->c_node.i_mode & SET_GID) udata.u_egid = ino->c_node.i_gid; /* FIXME: In the execve case we may on some platforms have space below PROGLOAD to clear... */ /* We are definitely going to succeed with the exec, * so we can start writing over the old program */ uput(hdr, (uint8_t *)progload, 16); /* At this point, we are committed to reading in and * executing the program. This call must not block. */ close_on_exec(); /* * Read in the rest of the program, block by block. We rely upon * the optimization path in readi to spot this is a big move to user * space and move it directly. */ progptr = progload + 16; if (bin_size > 16) { bin_size -= 16; udata.u_base = (uint8_t *)progptr; /* We copied the first block already */ udata.u_count = bin_size; udata.u_sysio = false; readi(ino, 0); if (udata.u_done != bin_size) goto nogood4; progptr += bin_size; } /* Wipe the memory in the BSS. We don't wipe the memory above that on 8bit boxes, but defer it to brk/sbrk() */ uzero((uint8_t *)progptr, bss); /* Set initial break for program */ udata.u_break = (int)ALIGNUP(progptr + bss); /* Turn off caught signals */ memset(udata.u_sigvec, 0, sizeof(udata.u_sigvec)); // place the arguments, environment and stack at the top of userspace memory, // Write back the arguments and the environment nargv = wargs(((char *) top - 2), abuf, &argc); nenvp = wargs((char *) (nargv), ebuf, NULL); // Fill in udata.u_name with program invocation name uget((void *) ugetw(nargv), udata.u_name, 8); memcpy(udata.u_ptab->p_name, udata.u_name, 8); tmpfree(abuf); tmpfree(ebuf); i_deref(ino); /* Shove argc and the address of argv just below envp FIXME: should flip them in crt0.S of app for R2L setups so we can get rid of the ifdefs */ #ifdef CONFIG_CALL_R2L /* Arguments are stacked the 'wrong' way around */ uputw((uint16_t) nargv, nenvp - 2); uputw((uint16_t) argc, nenvp - 1); #else uputw((uint16_t) nargv, nenvp - 1); uputw((uint16_t) argc, nenvp - 2); #endif /* Set stack pointer for the program */ udata.u_isp = nenvp - 2; /* Start execution (never returns) */ udata.u_ptab->p_status = P_RUNNING; doexec(progload); /* tidy up in various failure modes */ nogood4: /* Must not run userspace */ ssig(udata.u_ptab, SIGKILL); nogood3: udata.u_ptab->p_status = P_RUNNING; tmpfree(abuf); tmpfree(ebuf); nogood2: nogood: i_unlock_deref(ino); return (-1); }
inoptr kn_open(char *namep, inoptr *parent) { staticfast inoptr wd; /* the directory we are currently searching. */ staticfast inoptr ninode; inoptr temp; staticfast char *name; name = namep; #ifdef DEBUG kprintf("kn_open(\"%s\")\n", name); #endif if(*name == '/') wd = udata.u_root; else wd = udata.u_cwd; i_ref(ninode = wd); i_ref(ninode); for(;;) { if(ninode) magic(ninode); /* cheap way to spot rename inside yourself */ if (udata.u_rename == ninode) udata.u_rename = NULLINODE; /* See if we are at a mount point */ if(ninode) ninode = srch_mt(ninode); while(*name == '/') /* Skip(possibly repeated) slashes */ ++name; if(!*name) /* No more components of path? */ break; if(!ninode){ udata.u_error = ENOENT; goto nodir; } i_deref(wd); wd = ninode; if(getmode(wd) != F_DIR){ udata.u_error = ENOTDIR; goto nodir; } if(!(getperm(wd) & OTH_EX)){ udata.u_error = EPERM; goto nodir; } /* See if we are going up through a mount point */ if((wd == udata.u_root || (wd->c_num == ROOTINODE && wd->c_dev != root_dev)) && name[0] == '.' && name[1] == '.' && (name[2] == '/' || name[2] == '\0')){ if (wd == udata.u_root) { ninode = wd; name += 2; continue; } temp = fs_tab_get(wd->c_dev)->m_fs->s_mntpt; ++temp->c_refs; i_deref(wd); wd = temp; } ninode = srch_dir(wd, name); while(*name != '/' && *name) ++name; } if(parent) *parent = wd; else i_deref(wd); if(!(parent || ninode)) udata.u_error = ENOENT; return ninode; nodir: if(parent) *parent = NULLINODE; i_deref(wd); return NULLINODE; }
bool ch_link(inoptr wd, char *oldname, char *newname, inoptr nindex) { struct direct curentry; int i; if(!(getperm(wd) & OTH_WR)) { udata.u_error = EPERM; return false; } /* Inserting a new blank entry ? */ if (!*newname && nindex != NULLINODE) { udata.u_error = EEXIST; return false; } /* Search the directory for the desired slot. */ udata.u_offset = 0; for(;;) { udata.u_count = DIR_LEN; udata.u_base =(char *)&curentry; udata.u_sysio = true; readi(wd, 0); /* Read until EOF or name is found. readi() advances udata.u_offset */ if(udata.u_count == 0 || namecomp(oldname, curentry.d_name)) break; } if(udata.u_count == 0 && *oldname) return false; /* Entry not found */ memcpy(curentry.d_name, newname, FILENAME_LEN); // pad name with NULLs for(i = 0; i < FILENAME_LEN; ++i) if(curentry.d_name[i] == '\0') break; for(; i < FILENAME_LEN; ++i) curentry.d_name[i] = '\0'; if(nindex) curentry.d_ino = nindex->c_num; else curentry.d_ino = 0; /* If an existing slot is being used, we must back up the file offset */ if(udata.u_count){ udata.u_offset -= DIR_LEN; } udata.u_count = DIR_LEN; udata.u_base = (char*)&curentry; udata.u_sysio = true; writei(wd, 0); if(udata.u_error) return false; setftime(wd, A_TIME|M_TIME|C_TIME); /* Sets c_dirty */ /* Update file length to next block */ if(wd->c_node.i_size & BLKMASK) wd->c_node.i_size += BLKSIZE - (wd->c_node.i_size & BLKMASK); return true; // success }
arg_t _open(void) { int8_t uindex; int8_t oftindex; staticfast inoptr ino; int16_t perm; staticfast inoptr parent; char fname[FILENAME_LEN + 1]; int trunc; int r; int w; int j; parent = NULLINODE; trunc = flag & O_TRUNC; r = (flag + 1) & 1; w = (flag + 1) & 2; if (O_ACCMODE(flag) == 3 || (flag & O_BADBITS)) { udata.u_error = EINVAL; return (-1); } if ((uindex = uf_alloc()) == -1) return (-1); if ((oftindex = oft_alloc()) == -1) goto nooft; ino = n_open(name, &parent); if (ino) { i_deref(parent); /* O_EXCL test */ if ((flag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { udata.u_error = EEXIST; goto idrop; } } else { /* The n_open failed */ if (udata.u_error == EFAULT) goto cantopen; /* New file */ if (!(flag & O_CREAT)) { udata.u_error = ENOENT; goto cantopen; } filename(name, fname); /* newfile drops parent for us */ if (parent && (ino = newfile(parent, fname))) { ino->c_node.i_mode = (F_REG | (mode & MODE_MASK & ~udata.u_mask)); setftime(ino, A_TIME | M_TIME | C_TIME); wr_inode(ino); } else { udata.u_error = ENFILE; /* FIXME, should be set in newfile not bodged to a guessed code */ goto cantopen; } } /* Book our slot in case we block opening a device */ of_tab[oftindex].o_inode = ino; perm = getperm(ino); if ((r && !(perm & OTH_RD)) || (w && !(perm & OTH_WR))) { udata.u_error = EACCES; goto cantopen; } if (w) { if (getmode(ino) == F_DIR ) { udata.u_error = EISDIR; goto cantopen; } if (ino->c_flags & CRDONLY) { udata.u_error = EROFS; goto cantopen; } } if (isdevice(ino)) { inoptr *iptr = &of_tab[oftindex].o_inode; /* d_open may block and thus ino may become invalid as may parent (but we don't need it again). It may also be changed by the call to dev_openi */ if (dev_openi(iptr, flag) != 0) goto cantopen; /* May have changed */ /* get the static pointer back in case it changed via dev usage or just because we blocked */ ino = *iptr; } if (trunc && getmode(ino) == F_REG) { if (f_trunc(ino)) goto idrop; for (j = 0; j < OFTSIZE; ++j) /* Arguably should fix at read/write */ if (of_tab[j].o_inode == ino) of_tab[j].o_ptr = 0; } udata.u_files[uindex] = oftindex; of_tab[oftindex].o_ptr = 0; of_tab[oftindex].o_access = flag; /* Save the low bits only */ if (flag & O_CLOEXEC) udata.u_cloexec |= (1 << oftindex); /* FIXME: ATIME ? */ /* * Sleep process if no writer or reader */ if (getmode(ino) == F_PIPE && of_tab[oftindex].o_refs == 1 && !(flag & O_NDELAY)) psleep(ino); /* From the moment of the psleep ino is invalid */ return (uindex); idrop: i_deref(ino); cantopen: oft_deref(oftindex); /* This will call i_deref() */ nooft: udata.u_files[uindex] = NO_FILE; return (-1); }
arg_t _execve(void) { /* Not ideal on stack */ struct binfmt_flat binflat; inoptr ino; char **nargv; /* In user space */ char **nenvp; /* In user space */ struct s_argblk *abuf, *ebuf; int argc; uint32_t bin_size; /* Will need to be bigger on some cpus */ uaddr_t progbase, top; uaddr_t go; uint32_t true_brk; if (!(ino = n_open_lock(name, NULLINOPTR))) return (-1); if (!((getperm(ino) & OTH_EX) && (ino->c_node.i_mode & F_REG) && (ino->c_node.i_mode & (OWN_EX | OTH_EX | GRP_EX)))) { udata.u_error = EACCES; goto nogood; } setftime(ino, A_TIME); udata.u_offset = 0; udata.u_count = sizeof(struct binfmt_flat); udata.u_base = (void *)&binflat; udata.u_sysio = true; readi(ino, 0); if (udata.u_done != sizeof(struct binfmt_flat)) { udata.u_error = ENOEXEC; goto nogood; } /* FIXME: ugly - save this as valid_hdr modifies it */ true_brk = binflat.bss_end; /* Hard coded for our 68K format. We don't quite use the ucLinux names, we don't want to load a ucLinux binary in error! */ if (memcmp(binflat.magic, "bFLT", 4) || !valid_hdr(ino, &binflat)) { udata.u_error = ENOEXEC; goto nogood2; } /* Memory needed */ bin_size = binflat.bss_end + binflat.stack_size; /* Overflow ? */ if (bin_size < binflat.bss_end) { udata.u_error = ENOEXEC; goto nogood2; } /* Gather the arguments, and put them in temporary buffers. */ abuf = (struct s_argblk *) tmpbuf(); /* Put environment in another buffer. */ ebuf = (struct s_argblk *) tmpbuf(); /* Read args and environment from process memory */ if (rargs(argv, abuf) || rargs(envp, ebuf)) goto nogood3; /* This must be the last test as it makes changes if it works */ /* FIXME: need to update this to support split code/data and to fix stack handling nicely */ /* FIXME: ENOMEM fix needs to go to 16bit ? */ if ((udata.u_error = pagemap_realloc(0, bin_size, 0)) != 0) goto nogood3; /* Core dump and ptrace permission logic */ #ifdef CONFIG_LEVEL_2 /* Q: should uid == 0 mean we always allow core */ if ((!(getperm(ino) & OTH_RD)) || (ino->c_node.i_mode & (SET_UID | SET_GID))) udata.u_flags |= U_FLAG_NOCORE; else udata.u_flags &= ~U_FLAG_NOCORE; #endif udata.u_codebase = progbase = pagemap_base(); /* From this point on we are commmited to the exec() completing so we can start writing over the old program */ uput(&binflat, (uint8_t *)progbase, sizeof(struct binfmt_flat)); /* setuid, setgid if executable requires it */ if (ino->c_node.i_mode & SET_UID) udata.u_euid = ino->c_node.i_uid; if (ino->c_node.i_mode & SET_GID) udata.u_egid = ino->c_node.i_gid; top = progbase + bin_size; udata.u_top = top; udata.u_ptab->p_top = top; // kprintf("user space at %p\n", progbase); // kprintf("top at %p\n", progbase + bin_size); bin_size = binflat.reloc_start + 4 * binflat.reloc_count; go = (uint32_t)progbase + binflat.entry; close_on_exec(); /* * Read in the rest of the program, block by block. We rely upon * the optimization path in readi to spot this is a big move to user * space and move it directly. */ if (bin_size > sizeof(struct binfmt_flat)) { /* We copied the header already */ bin_size -= sizeof(struct binfmt_flat); udata.u_base = (uint8_t *)progbase + sizeof(struct binfmt_flat); udata.u_count = bin_size; udata.u_sysio = false; readi(ino, 0); if (udata.u_done != bin_size) goto nogood4; } /* Header isn't counted in relocations */ relocate(&binflat, progbase, bin_size); /* This may wipe the relocations */ uzero((uint8_t *)progbase + binflat.data_end, binflat.bss_end - binflat.data_end + binflat.stack_size); /* Use of brk eats into the stack allocation */ /* Use the temporary we saved (hack) as we mangled bss_end */ udata.u_break = udata.u_codebase + true_brk; /* Turn off caught signals */ memset(udata.u_sigvec, 0, sizeof(udata.u_sigvec)); /* place the arguments, environment and stack at the top of userspace memory. */ /* Write back the arguments and the environment */ nargv = wargs(((char *) top - 4), abuf, &argc); nenvp = wargs((char *) (nargv), ebuf, NULL); /* Fill in udata.u_name with Program invocation name */ uget((void *) ugetl(nargv, NULL), udata.u_name, 8); memcpy(udata.u_ptab->p_name, udata.u_name, 8); tmpfree(abuf); tmpfree(ebuf); i_unlock_deref(ino); /* Shove argc and the address of argv just below envp */ uputl((uint32_t) nargv, nenvp - 1); uputl((uint32_t) argc, nenvp - 2); // Set stack pointer for the program udata.u_isp = nenvp - 2; /* * Sort of - it's a good way to deal with all the stupidity of * random 68K platforms we will have to handle, and a nice place * to stuff the signal trampoline 8) */ install_vdso(); // kprintf("Go = %p ISP = %p\n", go, udata.u_isp); doexec(go); nogood4: /* Must not run userspace */ ssig(udata.u_ptab, SIGKILL); nogood3: tmpfree(abuf); tmpfree(ebuf); nogood2: nogood: i_unlock_deref(ino); return (-1); }