Npfcall* diod_lopen (Npfid *fid, u32 flags) { Fid *f = fid->aux; Npfcall *res = NULL; Npqid qid; u32 iounit = 0; /* client will use msize-P9_IOHDRSZ */ if ((f->xflags & XFLAGS_RO) && ((flags & O_WRONLY) || (flags & O_RDWR))) { np_uerror (EROFS); goto error_quiet; } if ((flags & O_CREAT)) /* can't happen? */ flags &= ~O_CREAT; /* clear and allow to fail with ENOENT */ if (_fidstat (f) < 0) goto error_quiet; if (S_ISDIR (f->stat.st_mode)) { f->dir = opendir (f->path); if (!f->dir) { np_uerror (errno); goto error_quiet; } } else { f->fd = open (f->path, flags); if (f->fd < 0) { np_uerror (errno); goto error_quiet; } } if (_fidstat (f) < 0) goto error; /* can't happen? */ _ustat2qid (&f->stat, &qid); //iounit = f->stat.st_blksize; if (!(res = np_create_rlopen (&qid, iounit))) { np_uerror (ENOMEM); goto error; } return res; error: errn (np_rerror (), "diod_lopen %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (f->dir) { (void)closedir (f->dir); f->dir = NULL; } if (f->fd != -1) { (void)close (f->fd); f->fd = -1; } if (res) free (res); return NULL; }
/* Locking note: * Implement POSIX locks in terms of BSD flock locks. * This at least gets distributed whole-file locking to work. * Strategies for distributed record locking will deadlock. */ Npfcall* diod_lock (Npfid *fid, u8 type, u32 flags, u64 start, u64 length, u32 proc_id, Npstr *client_id) { Fid *f = fid->aux; Npfcall *ret = NULL; u8 status = P9_LOCK_ERROR; if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if (flags & ~P9_LOCK_FLAGS_BLOCK) { /* only one valid flag for now */ np_uerror (EINVAL); /* (which we ignore) */ goto error; } if (_fidstat(f) < 0) goto error_quiet; switch (type) { case F_UNLCK: if (flock (f->fd, LOCK_UN) >= 0) { status = P9_LOCK_SUCCESS; f->lock_type = LOCK_UN; } else status = P9_LOCK_ERROR; break; case F_RDLCK: if (flock (f->fd, LOCK_SH | LOCK_NB) >= 0) { status = P9_LOCK_SUCCESS; f->lock_type = LOCK_SH; } else if (errno == EWOULDBLOCK) { status = P9_LOCK_BLOCKED; } else status = P9_LOCK_ERROR; break; case F_WRLCK: if (flock (f->fd, LOCK_EX | LOCK_NB) >= 0) { status = P9_LOCK_SUCCESS; f->lock_type = LOCK_EX; } else if (errno == EWOULDBLOCK) { status = P9_LOCK_BLOCKED; } break; default: np_uerror (EINVAL); goto error; } if (!((ret = np_create_rlock (status)))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_lock %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (ret) free (ret); return NULL; }
/* Twalk - walk a file path * Called from fcall.c::np_walk () on each wname component in succession. * On error, call np_uerror () and return 0. */ int diod_walk (Npfid *fid, Npstr* wname, Npqid *wqid) { Fid *f = fid->aux; struct stat st; char *npath; if (!(npath = _mkpath (f->path, wname))) { np_uerror (ENOMEM); goto error; } if (lstat (npath, &st) < 0) { np_uerror (errno); goto error_quiet; } /* N.B. inodes would not be unique if we could cross over to another * file system. But with the code below, ls -l returns ??? for mount * point dirs, which would otherwise have a "foreign" inode number. * How does NFS make them appear as empty directories? That would be * prettier. */ if (_fidstat (f) < 0) goto error; if (st.st_dev != f->stat.st_dev) { np_uerror (EXDEV); goto error; } free (f->path); f->path = npath; _ustat2qid (&st, wqid); return 1; error: errn (np_rerror (), "diod_walk %s@%s:%s/%.*s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path, wname->len, wname->str); error_quiet: if (npath) free (npath); return 0; }
/* Tattach - attach a new user (fid->user) to aname. * diod_auth.c::diod_checkauth first authenticates/authorizes user */ Npfcall* diod_attach (Npfid *fid, Npfid *afid, Npstr *aname) { Npfcall* ret = NULL; Fid *f = NULL; Npqid qid; if (aname->len == 0 || *aname->str != '/') { np_uerror (EPERM); goto error; } if (!(f = _fidalloc ()) || !(f->path = np_strdup (aname))) { np_uerror (ENOMEM); goto error; } if (diod_conf_opt_runasuid ()) { if (fid->user->uid != diod_conf_get_runasuid ()) { np_uerror (EPERM); goto error; } } if (!diod_match_exports (f->path, fid->conn, fid->user, &f->xflags)) goto error; if (_fidstat (f) < 0) goto error; _ustat2qid (&f->stat, &qid); if ((ret = np_create_rattach (&qid)) == NULL) { np_uerror (ENOMEM); goto error; } fid->aux = f; np_fid_incref (fid); return ret; error: errn (np_rerror (), "diod_attach %s@%s:%.*s", fid->user->uname, np_conn_get_client_id (fid->conn), aname->len, aname->str); if (f) _fidfree (f); return NULL; }
Npfcall* diod_getattr(Npfid *fid, u64 request_mask) { Fid *f = fid->aux; Npfcall *ret = NULL; Npqid qid; if (_fidstat (f) < 0) goto error_quiet; _ustat2qid (&f->stat, &qid); if (!(ret = np_create_rgetattr(request_mask, &qid, f->stat.st_mode, f->stat.st_uid, f->stat.st_gid, f->stat.st_nlink, f->stat.st_rdev, f->stat.st_size, f->stat.st_blksize, f->stat.st_blocks, f->stat.st_atim.tv_sec, f->stat.st_atim.tv_nsec, f->stat.st_mtim.tv_sec, f->stat.st_mtim.tv_nsec, f->stat.st_ctim.tv_sec, f->stat.st_ctim.tv_nsec, 0, 0, 0, 0))) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_getattr %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (ret) free (ret); return NULL; }
Npfcall* diod_setattr (Npfid *fid, u32 valid, u32 mode, u32 uid, u32 gid, u64 size, u64 atime_sec, u64 atime_nsec, u64 mtime_sec, u64 mtime_nsec) { Npfcall *ret = NULL; Fid *f = fid->aux; int fidstat_updated = 0; int ctime_updated = 0; if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if ((valid & P9_SETATTR_MODE) || (valid & P9_SETATTR_SIZE)) { if (_fidstat(f) < 0) goto error_quiet; fidstat_updated = 1; if (S_ISLNK(f->stat.st_mode)) { msg ("diod_setattr: unhandled mode/size update on symlink"); np_uerror(EINVAL); goto error; } } /* chmod (N.B. dereferences symlinks) */ if ((valid & P9_SETATTR_MODE)) { if (chmod (f->path, mode) < 0) { np_uerror(errno); goto error_quiet; } ctime_updated = 1; } /* chown */ if ((valid & P9_SETATTR_UID) || (valid & P9_SETATTR_GID)) { if (lchown (f->path, (valid & P9_SETATTR_UID) ? uid : -1, (valid & P9_SETATTR_GID) ? gid : -1) < 0) { np_uerror(errno); goto error_quiet; } ctime_updated = 1; } /* truncate (N.B. dereferences symlinks) */ if ((valid & P9_SETATTR_SIZE)) { if (truncate (f->path, size) < 0) { np_uerror(errno); goto error_quiet; } ctime_updated = 1; } /* utimes */ if ((valid & P9_SETATTR_ATIME) || (valid & P9_SETATTR_MTIME)) { #if HAVE_UTIMENSAT struct timespec ts[2]; if (!(valid & P9_SETATTR_ATIME)) { ts[0].tv_sec = 0; ts[0].tv_nsec = UTIME_OMIT; } else if (!(valid & P9_SETATTR_ATIME_SET)) { ts[0].tv_sec = 0; ts[0].tv_nsec = UTIME_NOW; } else { ts[0].tv_sec = atime_sec; ts[0].tv_nsec = atime_nsec; } if (!(valid & P9_SETATTR_MTIME)) { ts[1].tv_sec = 0; ts[1].tv_nsec = UTIME_OMIT; } else if (!(valid & P9_SETATTR_MTIME_SET)) { ts[1].tv_sec = 0; ts[1].tv_nsec = UTIME_NOW; } else { ts[1].tv_sec = mtime_sec; ts[1].tv_nsec = mtime_nsec; } if (utimensat(-1, f->path, ts, AT_SYMLINK_NOFOLLOW) < 0) { np_uerror(errno); goto error_quiet; } ctime_updated = 1; #else /* HAVE_UTIMENSAT */ struct timeval tv[2], now, *tvp; /* N.B. this utimes () implementation loses atomicity and precision. */ if ((valid & P9_SETATTR_ATIME) && !(valid & P9_SETATTR_ATIME_SET) && (valid & P9_SETATTR_MTIME) && !(valid & P9_SETATTR_MTIME_SET)) { tvp = NULL; /* set both to now */ } else { if (!fidstat_updated && _fidstat(f) < 0) goto error_quiet; fidstat_updated = 1; if (gettimeofday (&now, NULL) < 0) { np_uerror (errno); goto error_quiet; } if (!(valid & P9_SETATTR_ATIME)) { tv[0].tv_sec = f->stat.st_atim.tv_sec; tv[0].tv_usec = f->stat.st_atim.tv_nsec / 1000; } else if (!(valid & P9_SETATTR_ATIME_SET)) { tv[0].tv_sec = now.tv_sec; tv[0].tv_usec = now.tv_usec; } else { tv[0].tv_sec = atime_sec; tv[0].tv_usec = atime_nsec / 1000; } if (!(valid & P9_SETATTR_MTIME)) { tv[1].tv_sec = f->stat.st_mtim.tv_sec; tv[1].tv_usec = f->stat.st_mtim.tv_nsec / 1000; } else if (!(valid & P9_SETATTR_MTIME_SET)) { tv[1].tv_sec = now.tv_sec; tv[1].tv_usec = now.tv_usec; } else { tv[1].tv_sec = mtime_sec; tv[1].tv_usec = mtime_nsec / 1000; } tvp = tv; } if (utimes (f->path, tvp) < 0) { np_uerror(errno); goto error_quiet; } ctime_updated = 1; #endif } if ((valid & P9_SETATTR_CTIME) && !ctime_updated) { if (lchown (f->path, -1, -1) < 0) { np_uerror (errno); goto error_quiet; } } if (!(ret = np_create_rsetattr())) { np_uerror (ENOMEM); goto error; } return ret; error: errn (np_rerror (), "diod_setattr %s@%s:%s (valid=0x%x)", fid->user->uname, np_conn_get_client_id (fid->conn), f->path, valid); error_quiet: if (ret) free (ret); return NULL; }
Npfcall* diod_getlock (Npfid *fid, u8 type, u64 start, u64 length, u32 proc_id, Npstr *client_id) { Fid *f = fid->aux; Npfcall *ret = NULL; char *cid = NULL; if ((f->xflags & XFLAGS_RO)) { np_uerror (EROFS); goto error_quiet; } if (!(cid = np_strdup (client_id))) { np_uerror (ENOMEM); goto error; } if (_fidstat(f) < 0) goto error_quiet; switch (type) { case F_RDLCK: switch (f->lock_type) { case LOCK_EX: case LOCK_SH: type = LOCK_UN; break; case LOCK_UN: if (flock (f->fd, LOCK_SH | LOCK_NB) >= 0) { (void)flock (f->fd, LOCK_UN); type = LOCK_UN; } else type = LOCK_EX; break; } break; case F_WRLCK: switch (f->lock_type) { case LOCK_EX: type = LOCK_UN; break; case LOCK_SH: /* Rather than upgrade the lock to LOCK_EX and risk * not reacquiring the LOCK_SH afterwards, lie about * the lock being available. Getlock is racy anyway. */ type = LOCK_UN; break; case LOCK_UN: if (flock (f->fd, LOCK_EX | LOCK_NB) >= 0) { (void)flock (f->fd, LOCK_UN); type = LOCK_UN; } else type = LOCK_EX; /* could also be LOCK_SH actually */ } break; default: np_uerror (EINVAL); goto error; } if (type != LOCK_UN) { /* FIXME: need to fake up start, length, proc_id, cid? */ } if (!((ret = np_create_rgetlock(type, start, length, proc_id, cid)))) { np_uerror (ENOMEM); goto error; } free (cid); return ret; error: errn (np_rerror (), "diod_getlock %s@%s:%s", fid->user->uname, np_conn_get_client_id (fid->conn), f->path); error_quiet: if (ret) free (ret); if (cid) free (cid); return NULL; }