📄 tfs_subr.c
字号:
/* * Construct a pathname for a pnode. If a parent directory has an fd * open, fchdir to it and return the partial name; otherwise construct the * full pathname of the file. If a parent directory has a long name, then * fchdir to that directory (this saves on NFS remote lookup calls, since * the kernel directory name lookup cache only caches names of a small * length.) */voidptoname_or_pn(pp, pn) struct pnode *pp; char *pn;{ char tpn[MAXPATHLEN]; char *pnptr = &tpn[MAXPATHLEN - 1]; int pathsize = 0; if ((pp->p_fd != 0) && (pp->p_type == PTYPE_DIR) && (change_to_dir(pp) == 0)) { strcpy(pn, "."); return; } *pnptr = '\0'; do { pnptr = prepend(pp->p_name, pnptr, &pathsize); pp = PARENT_PNODE(pp); if (pp->p_fd != 0 || strlen(pp->p_name) >= 20) { if (change_to_dir(pp) == 0) { break; } } pnptr = prepend("/", pnptr, &pathsize); } while (pp->p_name[0] != '/'); strcpy(pn, pnptr);}/* * Convert pnode to full pathname. */voidptopn(pp, pn) struct pnode *pp; char *pn;{ ntree_pathname((struct tnode *) pp, pn);}/* * Convert vnode to virtual pathname. */voidvtovn(vp, pn) struct vnode *vp; char *pn;{ ntree_pathname((struct tnode *) vp, pn);}/* * prepend() tacks a directory name onto the front of a pathname. * The maximum length of the pathname is MAXPATHLEN. */char *prepend(dirname, pathname, pathsize) char *dirname; char *pathname; int *pathsize;{ int i; /* directory name size counter */ for (i = 0; *dirname != '\0'; i++, dirname++) continue; if ((*pathsize += i) < MAXPATHLEN) while (i-- > 0) *--pathname = *--dirname; return (pathname);}/* * Alarm routines, for timing out the attribute & write caches */#define ALARM_DELAY 5 /* # of secs between alarms */static struct timeval current_time;bool_t alarm_went_off;extern bool_t servicing_req;longget_current_time(precise) bool_t precise;{ struct timeval time; if (current_time.tv_sec == 0) {#ifdef TFSDEBUG dprint(tfsdebug, 3, "Setting alarm\n");#endif TFSDEBUG (void) gettimeofday(¤t_time, (struct timezone *) NULL); alarm(ALARM_DELAY); } else if (precise) { (void) gettimeofday(&time, (struct timezone *) NULL); return (time.tv_sec); } return (current_time.tv_sec);}/* ARGSUSED */voidalarm_handler(sig, code, scp) int sig; int code; struct sigcontext *scp;{ bool_t more_attrs; bool_t more_writes; if (servicing_req) { /* * Handle this alarm after the current request is serviced. * (This avoids race conditions between the attr and write * cache timeout routines and other routines which modify * the caches.) */ alarm_went_off = TRUE; return; } (void) gettimeofday(¤t_time, (struct timezone *) NULL);#ifdef TFSDEBUG dprint(tfsdebug, 3, "ALARM = %u\n", current_time.tv_sec);#endif TFSDEBUG more_attrs = timeout_attr_cache(current_time.tv_sec); more_writes = timeout_wcache(current_time.tv_sec); if ((more_attrs) || (more_writes)) { alarm(ALARM_DELAY); } else { current_time.tv_sec = 0; } alarm_went_off = FALSE;}intinit_alarm(){ (void) signal(SIGALRM, alarm_handler);}/* * Routines which keep track of the process's user credentials. *//* * Declare this struct rather than use 'struct ucred' because the fields * of ucred are shorts in 4.0. This will cause problems if the uid or * gid is ever negative, because setreuid & setregid barf if given a * negative number other than -1. (An example negative uid/gid is -2 * for 'nobody'.) */static struct { u_short cr_uid; /* effective user id */ u_short cr_gid; /* effective group id */ int cr_groups[NGROUPS]; /* groups, 0 terminated */} user_cred;static int num_groups; /* Length of group list *//* * Change user id */voidchange_user_id(uid) int uid;{ setreuid(-1, 0); setreuid(-1, uid);}intcurrent_user_id(){ return (user_cred.cr_uid);}/* * Set uid, gid, and gids to auth params that came in over the wire. Call * setreuid only if the set of auth params is different from the current set * of user credentials. NOTE: any other routine which does a setreuid() * must make sure to setreuid() back to the effective user id, otherwise * chaos could result! */intset_user_id(aup) struct authunix_parms *aup;{#ifdef TFSDEBUG dprint(tfsdebug, 3, "(uid %d gid %d) ", aup->aup_uid, aup->aup_gid);#endif TFSDEBUG /* * If the user changes groups on us, we won't catch it here. */ if (aup->aup_uid == user_cred.cr_uid) { return (0); } if (setreuid(-1, 0)) { return (-1); } user_cred.cr_gid = aup->aup_gid; if (setregid(-1, (int) user_cred.cr_gid)) { return (-1); } num_groups = aup->aup_len; BCOPY((caddr_t) aup->aup_gids, (caddr_t) user_cred.cr_groups, num_groups * sizeof (int)); if (setgroups((int) num_groups, (int *) aup->aup_gids)) { return (-1); } user_cred.cr_uid = aup->aup_uid; if (setreuid(-1, (int) user_cred.cr_uid)) { return (-1); } return (0);}/* * This routine is called by tfs_setattr() to ensure that the user has * the correct access to file 'vp' to set the attributes specified in * 'sa'. This routine is necessary because the file may have to be * promoted to the front file system before the attributes are set, and * we don't want to copy the file if the setattr is going to fail. */inthas_perm_to_setattr(vp, sa) struct vnode *vp; struct nfssattr *sa;{ struct nfsfattr attrs; if (getattrs_of(vp, &attrs) < 0) { return (errno); } /* * Change file mode or time. Must be owner of file. */ if ((sa->sa_mode != (u_short) -1 && sa->sa_mode != -1) || sa->sa_atime.tv_sec != -1 || sa->sa_mtime.tv_sec != -1) { if (user_cred.cr_uid != attrs.na_uid) { return (EPERM); } } /* * Change file/group ownership. Must be su. */ if (sa->sa_uid != -1 && sa->sa_uid != attrs.na_uid && user_cred.cr_uid != 0) { return (EPERM); } if (sa->sa_gid != -1 && sa->sa_gid != attrs.na_gid && !in_group_list((int) sa->sa_gid) && user_cred.cr_uid != 0) { return (EPERM); } /* * Truncate file. Must have write access to the file and the * node cannot be a directory. */ if (sa->sa_size != -1) { if (vp->v_pnode->p_type == PTYPE_DIR || !has_access(&attrs, W_OK)) { return (EACCES); } } return (0);}/* * Check for accessibility of the file with attributes 'attrs', given * the credentials of the user. 'mode' is one of R_OK, W_OK, X_OK, or * an inclusive OR of them. This function returns TRUE if the file can * be accessed by the user. This function duplicates the functionality * of the access() system call. We can't use the access() system call * directly because access() would use the tfsd's real user id and real * group id, and we want to test for access using the effective uid & gid. */bool_thas_access(attrs, mode) struct nfsfattr *attrs; int mode;{ /* * If you're the super-user, you always get access. */ if (user_cred.cr_uid == 0) { return (TRUE); } /* * Access check is based on only one of owner, group, public. * If not owner, then check group. If not a member of the group, then * check public access. */ mode <<= 6; if (user_cred.cr_uid != attrs->na_uid) { mode >>= 3; if (!in_group_list((int) attrs->na_gid)) { mode >>= 3; } } return ((attrs->na_mode & mode) == mode);}static bool_tin_group_list(gid) int gid;{ int i; if (gid == user_cred.cr_gid) { return (TRUE); } for (i = 0; i < num_groups; i++) { if (gid == user_cred.cr_groups[i]) { return (TRUE); } } return (FALSE);}/* * Wrapper routines for NSE library routines which manipulate .tfs_info * files. These wrapper routines allow the tfsd to print an error message * before returning a cryptic error to the client (e.g. the tfsd may * return EPERM when it doesn't have permission to open .tfs_info, when the * client is doing an operation for which the error EPERM doesn't make sense.) */inttfsd_set_searchlink(directory, name, pp) char *directory; char *name; struct pnode *pp;{ Nse_err err; if (err = nse_set_searchlink(directory, name)) { nse_log_message("warning: can't set searchlink: "); tfsd_err_print(err); if (pp) { print_pnode_path(pp); } return (err->code); } return (0);}inttfsd_set_whiteout(directory, name, pp) char *directory; char *name; struct pnode *pp;{ Nse_err err; if (err = nse_set_whiteout(directory, name)) { nse_log_message("warning: can't set whiteout: "); tfsd_err_print(err); if (pp) { print_pnode_path(pp); } return (err->code); } return (0);}inttfsd_clear_whiteout(directory, name, pp) char *directory; char *name; struct pnode *pp;{ Nse_err err; if (err = nse_clear_whiteout(directory, name)) { nse_log_message("warning: can't clear whiteout: "); tfsd_err_print(err); if (pp) { print_pnode_path(pp); } return (err->code); } return (0);}inttfsd_get_backlink(directory, blp) char *directory; Nse_whiteout *blp;{ Nse_err err; if (err = nse_get_backlink(directory, blp)) { nse_log_message("warning: can't get backlink: "); tfsd_err_print(err); return (err->code); } return (0);}inttfsd_set_backlink(directory, name) char *directory; char *name;{ Nse_err err; if (err = nse_set_backlink(directory, name)) { nse_log_message("warning: can't set backlink: "); tfsd_err_print(err); return (err->code); } return (0);}inttfsd_get_tfs_info(directory, name, blp, wop, will_write_later, pp, ignore_enoent) char *directory; char *name; Nse_whiteout *blp; Nse_whiteout *wop; bool_t will_write_later; struct pnode *pp; bool_t ignore_enoent; /* ignore ENOENT error? */{ Nse_err err; if (err = nse_get_tfs_info(directory, name, blp, wop, will_write_later)) { if (ignore_enoent && err->code == ENOENT) { return (0); } nse_log_message("warning: nse_get_tfs_info: "); tfsd_err_print(err); if (pp) { print_pnode_path(pp); } return (err->code); } return (0);}inttfsd_set_tfs_info(directory, name, bl_first, wo, pp) char *directory; char *name; Nse_whiteout bl_first; Nse_whiteout wo; struct pnode *pp;{ Nse_err err; if (err = nse_set_tfs_info(directory, name, bl_first, wo)) { nse_log_message("warning: nse_set_tfs_info: "); tfsd_err_print(err); if (pp) { print_pnode_path(pp); } return (err->code); } return (0);}/* * Miscellaneous *//* * Modify the file with pnode 'pp' with the function 'ffunc' if the file * has a file descriptor, or with 'func' if the file doesn't have an fd. *//* VARARGS4 */intmodify_file(pp, func, ffunc, arg1, arg2) struct pnode *pp; int (* func)(); int (* ffunc)(); int arg1; int arg2;{ char name[MAXNAMLEN]; if (pp->p_fd != 0) { if (ffunc(pp->p_fd, arg1, arg2) < 0) { return (errno); } } else { ptoname(pp, name); if (func(name, arg1, arg2) < 0) { return (errno); } } return (0);}/* * Set the modify and access times of the file with vnode 'vp' to the * indicated values. If 'vp' is in a writeable layer, directly modify * the file; otherwise set the mtime in .tfs_backfiles and set the atime * of the read-only file. */intdo_utimes(vp, mtime, atime, set_to_now) struct vnode *vp; long mtime; long atime; bool_t set_to_now;{ struct pnode *parentp; struct timeval tvp[2]; struct nfsfattr attrs; char *fname; char name[MAXNAMLEN]; int result; if (vp->v_is_mount_pt) { parentp = vp->v_pnode; fname = "."; } else { parentp = get_front_parent_pnode(PARENT_VNODE(vp), vp->v_layer); fname = vp->v_name; } if (IS_WRITEABLE(vp)) { if (result = change_to_dir(parentp)) { return (result); } if (set_to_now) { if (utimes(fname, (struct timeval *)0) < 0) { return (errno); } } else { tvp[0].tv_sec = atime; tvp[0].tv_usec = 0; tvp[1].tv_sec = mtime;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -