int _9p_readlink(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; struct _9p_fid *pfid = NULL; cache_inode_status_t cache_status; struct gsh_buffdesc link_buffer = {.addr = NULL, .len = 0 }; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TREADLINK: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); /* let's do the job */ cache_status = cache_inode_readlink(pfid->pentry, &link_buffer); if (cache_status != CACHE_INODE_SUCCESS) return _9p_rerror(req9p, msgtag, _9p_tools_errno(cache_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RREADLINK); _9p_setptr(cursor, msgtag, u16); _9p_setstr(cursor, link_buffer.len - 1, link_buffer.addr); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RREADLINK: tag=%u fid=%u link=%s", *msgtag, (u32) *fid, (char *) link_buffer.addr); gsh_free(link_buffer.addr); return 1; }
int _9p_xattrcreate(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; int create; u16 *msgtag = NULL; u32 *fid = NULL; u64 *size; u32 *flag; u16 *name_len; char *name_str; struct _9p_fid *pfid = NULL; fsal_status_t fsal_status = { .major = ERR_FSAL_NO_ERROR, .minor = 0 }; char name[MAXNAMLEN+1]; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, size, u64); _9p_getptr(cursor, flag, u32); LogDebug(COMPONENT_9P, "TXATTRCREATE: tag=%u fid=%u name=%.*s size=%llu flag=%u", (u32) *msgtag, *fid, *name_len, name_str, (unsigned long long)*size, *flag); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); if (*size > _9P_XATTR_MAX_SIZE) return _9p_rerror(req9p, msgtag, ENOSPC, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } /* set op_ctx, it will be useful if FSAL is later called */ _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(name, sizeof(name), "%.*s", *name_len, name_str); if (*size == 0LL) { /* Size == 0 : this is in fact a call to removexattr */ LogDebug(COMPONENT_9P, "TXATTRCREATE: tag=%u fid=%u : will remove xattr %s", (u32) *msgtag, *fid, name); fsal_status = pfid->pentry->obj_ops.remove_extattr_by_name(pfid->pentry, name); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } else { /* Size != 0 , this is a creation/replacement of xattr */ /* Create the xattr at the FSAL level and cache result */ pfid->xattr = gsh_malloc(sizeof(*pfid->xattr) + *size); pfid->xattr->xattr_size = *size; pfid->xattr->xattr_offset = 0LL; pfid->xattr->xattr_write = _9P_XATTR_CAN_WRITE; strncpy(pfid->xattr->xattr_name, name, MAXNAMLEN); /* /!\ POSIX_ACL RELATED HOOK * Setting a POSIX ACL (using setfacl for example) means * settings a xattr named system.posix_acl_access BUT this * attribute is to be used and should not be created * (it exists already since acl feature is on) */ if (!strncmp(name, "system.posix_acl_access", MAXNAMLEN)) goto skip_create; /* try to create if flag doesn't have REPLACE bit */ if ((*flag & XATTR_REPLACE) == 0) create = true; else create = false; fsal_status = pfid->pentry->obj_ops.setextattr_value(pfid->pentry, name, pfid->xattr->xattr_content, *size, create); /* Try again with create = false if flag was set to 0 * and create failed because attribute already exists */ if (FSAL_IS_ERROR(fsal_status) && fsal_status.major == ERR_FSAL_EXIST && (*flag == 0)) { fsal_status = pfid->pentry->obj_ops.setextattr_value(pfid->pentry, name, pfid->xattr->xattr_content, *size, false); } if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pfid->xattr); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } } skip_create: /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RXATTRCREATE); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RXATTRCREATE: tag=%u fid=%u name=%.*s size=%llu flag=%u", (u32) *msgtag, *fid, *name_len, name_str, (unsigned long long)*size, *flag); return 1; } /* _9p_xattrcreate */
int _9p_lcreate(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *flags = NULL; u32 *mode = NULL; u32 *gid = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_newfile; u32 iounit = _9P_IOUNIT; struct fsal_obj_handle *pentry_newfile = NULL; char file_name[MAXNAMLEN+1]; fsal_status_t fsal_status; fsal_openflags_t openflags = 0; struct attrlist sattr; fsal_verifier_t verifier; enum fsal_create_mode createmode = FSAL_UNCHECKED; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, flags, u32); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TLCREATE: tag=%u fid=%u name=%.*s flags=0%o mode=0%o gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *flags, *mode, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(file_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(file_name, sizeof(file_name), "%.*s", *name_len, name_str); _9p_openflags2FSAL(flags, &openflags); pfid->state->state_data.fid.share_access = _9p_openflags_to_share_access(flags); memset(&verifier, 0, sizeof(verifier)); memset(&sattr, 0, sizeof(sattr)); sattr.valid_mask = ATTR_MODE | ATTR_GROUP; sattr.mode = *mode; sattr.group = *gid; if (*flags & 0x10) { /* Filesize is already 0. */ sattr.valid_mask |= ATTR_SIZE; } if (*flags & 0x1000) { /* If OEXCL, use FSAL_EXCLUSIVE_9P create mode * so that we can pass the attributes specified * above. Verifier is ignored for this create mode * because we don't have to deal with retry. */ createmode = FSAL_EXCLUSIVE_9P; } fsal_status = fsal_open2(pfid->pentry, pfid->state, openflags, createmode, file_name, &sattr, verifier, &pentry_newfile, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* put parent directory entry */ pfid->pentry->obj_ops->put_ref(pfid->pentry); /* Build the qid */ qid_newfile.type = _9P_QTFILE; qid_newfile.version = 0; qid_newfile.path = pentry_newfile->fileid; /* The fid will represent the new file now - we can't fail anymore */ pfid->pentry = pentry_newfile; pfid->qid = qid_newfile; pfid->xattr = NULL; pfid->opens = 1; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RLCREATE); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_newfile); _9p_setvalue(cursor, iounit, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RLCREATE: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu) iounit=%u pentry=%p", (u32) *msgtag, *fid, *name_len, name_str, qid_newfile.type, qid_newfile.version, (unsigned long long)qid_newfile.path, iounit, pfid->pentry); return 1; }
int _9p_mkdir(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *mode = NULL; u32 *gid = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_newdir; struct fsal_obj_handle *pentry_newdir = NULL; char dir_name[MAXNAMLEN+1]; fsal_status_t fsal_status; struct attrlist sattr; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TMKDIR: tag=%u fid=%u name=%.*s mode=0%o gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *mode, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(dir_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(dir_name, sizeof(dir_name), "%.*s", *name_len, name_str); fsal_prepare_attrs(&sattr, ATTR_MODE); sattr.mode = *mode; sattr.valid_mask = ATTR_MODE; /* Create the directory */ /* BUGAZOMEU: @todo : the gid parameter is not used yet */ fsal_status = fsal_create(pfid->pentry, dir_name, DIRECTORY, &sattr, NULL, &pentry_newdir, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); pentry_newdir->obj_ops.put_ref(pentry_newdir); /* Build the qid */ qid_newdir.type = _9P_QTDIR; qid_newdir.version = 0; qid_newdir.path = pentry_newdir->fileid; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RMKDIR); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_newdir); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RMKDIR: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu)", (u32) *msgtag, *fid, *name_len, name_str, qid_newdir.type, qid_newdir.version, (unsigned long long)qid_newdir.path); return 1; }
int _9p_setattr(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *valid = NULL; u32 *mode = NULL; u32 *uid = NULL; u32 *gid = NULL; u64 *size = NULL; u64 *atime_sec = NULL; u64 *atime_nsec = NULL; u64 *mtime_sec = NULL; u64 *mtime_nsec = NULL; struct _9p_fid *pfid = NULL; struct attrlist fsalattr; fsal_status_t fsal_status; struct timeval t; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, valid, u32); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, uid, u32); _9p_getptr(cursor, gid, u32); _9p_getptr(cursor, size, u64); _9p_getptr(cursor, atime_sec, u64); _9p_getptr(cursor, atime_nsec, u64); _9p_getptr(cursor, mtime_sec, u64); _9p_getptr(cursor, mtime_nsec, u64); LogDebug(COMPONENT_9P, "TSETATTR: tag=%u fid=%u valid=0x%x mode=0%o uid=%u gid=%u size=%" PRIu64 " atime=(%llu|%llu) mtime=(%llu|%llu)", (u32) *msgtag, *fid, *valid, *mode, *uid, *gid, *size, (unsigned long long)*atime_sec, (unsigned long long)*atime_nsec, (unsigned long long)*mtime_sec, (unsigned long long)*mtime_nsec); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* If a "time" change is required, but not with the "_set" suffix, * use gettimeofday */ if (*valid & (_9P_SETATTR_ATIME | _9P_SETATTR_CTIME | _9P_SETATTR_MTIME)) { if (gettimeofday(&t, NULL) == -1) { LogMajor(COMPONENT_9P, "TSETATTR: tag=%u fid=%u ERROR !! gettimeofday returned -1 with errno=%u", (u32) *msgtag, *fid, errno); return _9p_rerror(req9p, msgtag, errno, plenout, preply); } } /* Let's do the job */ memset((char *)&fsalattr, 0, sizeof(fsalattr)); if (*valid & _9P_SETATTR_MODE) { FSAL_SET_MASK(fsalattr.mask, ATTR_MODE); fsalattr.mode = *mode; } if (*valid & _9P_SETATTR_UID) { FSAL_SET_MASK(fsalattr.mask, ATTR_OWNER); fsalattr.owner = *uid; } if (*valid & _9P_SETATTR_GID) { FSAL_SET_MASK(fsalattr.mask, ATTR_GROUP); fsalattr.group = *gid; } if (*valid & _9P_SETATTR_SIZE) { FSAL_SET_MASK(fsalattr.mask, ATTR_SIZE); fsalattr.filesize = *size; } if (*valid & _9P_SETATTR_ATIME) { FSAL_SET_MASK(fsalattr.mask, ATTR_ATIME); fsalattr.atime.tv_sec = t.tv_sec; fsalattr.atime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_MTIME) { FSAL_SET_MASK(fsalattr.mask, ATTR_MTIME); fsalattr.mtime.tv_sec = t.tv_sec; fsalattr.mtime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_CTIME) { FSAL_SET_MASK(fsalattr.mask, ATTR_CTIME); fsalattr.ctime.tv_sec = t.tv_sec; fsalattr.ctime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_ATIME_SET) { FSAL_SET_MASK(fsalattr.mask, ATTR_ATIME); fsalattr.atime.tv_sec = *atime_sec; fsalattr.atime.tv_nsec = *atime_nsec; } if (*valid & _9P_SETATTR_MTIME_SET) { FSAL_SET_MASK(fsalattr.mask, ATTR_MTIME); fsalattr.mtime.tv_sec = *mtime_sec; fsalattr.mtime.tv_nsec = *mtime_nsec; } /* Now set the attr */ fsal_status = fsal_setattr(pfid->pentry, false, pfid->state, &fsalattr); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&fsalattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RSETATTR); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RSETATTR: tag=%u fid=%u valid=0x%x mode=0%o uid=%u gid=%u size=%" PRIu64 " atime=(%llu|%llu) mtime=(%llu|%llu)", (u32) *msgtag, *fid, *valid, *mode, *uid, *gid, *size, (unsigned long long)*atime_sec, (unsigned long long)*atime_nsec, (unsigned long long)*mtime_sec, (unsigned long long)*mtime_nsec); /* _9p_stat_update(*pmsgtype, TRUE, &pwkrdata->stats._9p_stat_req); */ return 1; }
int _9p_write(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u64 *offset = NULL; u32 *count = NULL; u32 outcount = 0; struct _9p_fid *pfid = NULL; size_t size; size_t written_size = 0; char *databuffer = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, offset, u64); _9p_getptr(cursor, count, u32); databuffer = cursor; LogDebug(COMPONENT_9P, "TWRITE: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Make sure the requested amount of data respects negotiated msize */ if (*count + _9P_ROOM_TWRITE > req9p->pconn->msize) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* Do the job */ size = *count; if (pfid->xattr != NULL) { if (*offset > pfid->xattr->xattr_size) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); if (pfid->xattr->xattr_write != _9P_XATTR_CAN_WRITE && pfid->xattr->xattr_write != _9P_XATTR_DID_WRITE) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); written_size = MIN(*count, pfid->xattr->xattr_size - *offset); memcpy(pfid->xattr->xattr_content + *offset, databuffer, written_size); pfid->xattr->xattr_offset += size; pfid->xattr->xattr_write = _9P_XATTR_DID_WRITE; /* ADD CODE TO DETECT GAP */ #if 0 fsal_status = pfid->pentry->ops->setextattr_value_by_id( pfid->pentry, &pfid->op_context, pfid->xattr->xattr_id, xattrval, size + 1); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); #endif outcount = written_size; } else { struct _9p_write_data write_data; struct fsal_io_arg *write_arg = alloca(sizeof(*write_arg) + sizeof(struct iovec)); write_arg->info = NULL; write_arg->state = pfid->state; write_arg->offset = *offset; write_arg->iov_count = 1; write_arg->iov[0].iov_len = size; write_arg->iov[0].iov_base = databuffer; write_arg->io_amount = 0; write_arg->fsal_stable = false; write_data.client = req9p->pconn->client; /* Do the actual write */ pfid->pentry->obj_ops->write2(pfid->pentry, true, _9p_write_cb, write_arg, &write_data); if (FSAL_IS_ERROR(write_data.ret)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(write_data.ret), plenout, preply); outcount = (u32) write_arg->io_amount; } /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RWRITE); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, outcount, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RWRITE: tag=%u fid=%u offset=%llu input count=%u output count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count, outcount); /** * @todo write statistics accounting goes here * modeled on nfs I/O stats */ return 1; }
int _9p_read(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; char *databuffer; u16 *msgtag = NULL; u32 *fid = NULL; u64 *offset = NULL; u32 *count = NULL; u32 outcount = 0; struct _9p_fid *pfid = NULL; size_t read_size = 0; bool eof_met; fsal_status_t fsal_status; /* uint64_t stable_flag = CACHE_INODE_SAFE_WRITE_TO_FS; */ bool sync = false; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, offset, u64); _9p_getptr(cursor, count, u32); LogDebug(COMPONENT_9P, "TREAD: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Make sure the requested amount of data respects negotiated msize */ if (*count + _9P_ROOM_RREAD > req9p->pconn->msize) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); /* Start building the reply already * So we don't need to use an intermediate data buffer */ _9p_setinitptr(cursor, preply, _9P_RREAD); _9p_setptr(cursor, msgtag, u16); databuffer = _9p_getbuffertofill(cursor); /* Do the job */ if (pfid->xattr != NULL) { /* Copy the value cached during xattrwalk */ if (*offset > pfid->xattr->xattr_size) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); if (pfid->xattr->xattr_write != _9P_XATTR_READ_ONLY) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); read_size = MIN(*count, pfid->xattr->xattr_size - *offset); memcpy(databuffer, pfid->xattr->xattr_content + *offset, read_size); outcount = read_size; } else { if (pfid->pentry->fsal->m_ops.support_ex(pfid->pentry)) { /* Call the new fsal_read */ fsal_status = fsal_read2(pfid->pentry, false, pfid->state, *offset, *count, &read_size, databuffer, &eof_met, NULL); } else { /* Call legacy fsal_rdwr */ fsal_status = fsal_rdwr(pfid->pentry, FSAL_IO_READ, *offset, *count, &read_size, databuffer, &eof_met, &sync, NULL); } /* Get the handle, for stats */ struct gsh_client *client = req9p->pconn->client; if (client == NULL) { LogDebug(COMPONENT_9P, "Cannot get client block for 9P request"); } else { op_ctx->client = client; server_stats_io_done(*count, read_size, FSAL_IS_ERROR(fsal_status), false); } if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); outcount = (u32) read_size; } _9p_setfilledbuffer(cursor, outcount); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RREAD: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); /** * @todo read statistics accounting goes here * modeled on nfs I/O statistics */ return 1; }
int _9p_remove(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; struct _9p_fid *pfid = NULL; cache_inode_status_t cache_status; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TREMOVE: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); cache_status = cache_inode_remove(pfid->ppentry, pfid->name); if (cache_status != CACHE_INODE_SUCCESS) return _9p_rerror(req9p, msgtag, _9p_tools_errno(cache_status), plenout, preply); /* If object is an opened file, close it */ if ((pfid->pentry->type == REGULAR_FILE) && is_open(pfid->pentry)) { if (pfid->opens) { cache_inode_dec_pin_ref(pfid->pentry, false); pfid->opens = 0; /* dead */ /* Under this flag, pin ref is still checked */ cache_status = cache_inode_close(pfid->pentry, CACHE_INODE_FLAG_REALLYCLOSE); if (cache_status != CACHE_INODE_SUCCESS) { FREE_FID(pfid, fid, req9p); return _9p_rerror(req9p, msgtag, _9p_tools_errno(cache_status), plenout, preply); } } } /* Clean the fid */ FREE_FID(pfid, fid, req9p); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RREMOVE); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "TREMOVE: tag=%u fid=%u", (u32) *msgtag, *fid); /* _9p_stat_update( *pmsgtype, TRUE, &pwkrdata->stats._9p_stat_req); */ return 1; }
int _9p_xattrwalk(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *attrfid = NULL; u16 *name_len; char *name_str; size_t attrsize = 0; fsal_status_t fsal_status; char name[MAXNAMLEN]; fsal_xattrent_t xattrs_arr[XATTRS_ARRAY_LEN]; int eod_met = false; unsigned int nb_xattrs_read = 0; unsigned int i = 0; char *xattr_cursor = NULL; unsigned int tmplen = 0; struct _9p_fid *pfid = NULL; struct _9p_fid *pxattrfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, attrfid, u32); LogDebug(COMPONENT_9P, "TXATTRWALK: tag=%u fid=%u attrfid=%u", (u32) *msgtag, *fid, *attrfid); _9p_getstr(cursor, name_len, name_str); if (*name_len == 0) LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=(LIST XATTR)", (u32) *msgtag, *fid, *attrfid); else LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=%.*s", (u32) *msgtag, *fid, *attrfid, *name_len, name_str); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); if (*attrfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } pxattrfid = gsh_calloc(1, sizeof(struct _9p_fid)); if (pxattrfid == NULL) return _9p_rerror(req9p, msgtag, ENOMEM, plenout, preply); /* set op_ctx, it will be useful if FSAL is later called */ _9p_init_opctx(pfid, req9p); /* Initiate xattr's fid by copying file's fid in it */ memcpy((char *)pxattrfid, (char *)pfid, sizeof(struct _9p_fid)); snprintf(name, MAXNAMLEN, "%.*s", *name_len, name_str); pxattrfid->specdata.xattr.xattr_content = gsh_malloc(XATTR_BUFFERSIZE); if (pxattrfid->specdata.xattr.xattr_content == NULL) { gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ENOMEM, plenout, preply); } if (*name_len == 0) { /* xattrwalk is used with an empty name, * this is a listxattr request */ fsal_status = pxattrfid->pentry->obj_handle->obj_ops.list_ext_attrs( pxattrfid->pentry->obj_handle, FSAL_XATTR_RW_COOKIE, /* Start with RW cookie, * hiding RO ones */ xattrs_arr, XATTRS_ARRAY_LEN, /** @todo fix static length */ &nb_xattrs_read, &eod_met); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); } /* if all xattrent are not read, * returns ERANGE as listxattr does */ if (eod_met != true) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); } xattr_cursor = pxattrfid->specdata.xattr.xattr_content; attrsize = 0; for (i = 0; i < nb_xattrs_read; i++) { tmplen = snprintf(xattr_cursor, MAXNAMLEN, "%s", xattrs_arr[i].xattr_name); xattr_cursor[tmplen] = '\0'; /* Just to be sure */ /* +1 for trailing '\0' */ xattr_cursor += tmplen + 1; attrsize += tmplen + 1; /* Make sure not to go beyond the buffer */ if (attrsize > XATTR_BUFFERSIZE) { gsh_free(pxattrfid->specdata.xattr. xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); } } } else { /* xattrwalk has a non-empty name, use regular setxattr */ fsal_status = pxattrfid->pentry->obj_handle->obj_ops. getextattr_id_by_name(pxattrfid->pentry->obj_handle, name, &pxattrfid->specdata.xattr.xattr_id); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); /* ENOENT for xattr is ENOATTR */ if (fsal_status.major == ERR_FSAL_NOENT) return _9p_rerror(req9p, msgtag, ENOATTR, plenout, preply); else return _9p_rerror(req9p, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); } fsal_status = pxattrfid->pentry->obj_handle->obj_ops. getextattr_value_by_name(pxattrfid->pentry->obj_handle, name, pxattrfid->specdata.xattr. xattr_content, XATTR_BUFFERSIZE, &attrsize); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); /* fsal_status.minor is a valid errno code */ return _9p_rerror(req9p, msgtag, fsal_status.minor, plenout, preply); } } req9p->pconn->fids[*attrfid] = pxattrfid; /* Increments refcount as we're manually making a new copy */ (void) cache_inode_lru_ref(pfid->pentry, LRU_REQ_STALE_OK); /* hold reference on gdata */ uid2grp_hold_group_data(pxattrfid->gdata); get_gsh_export_ref(pfid->export); get_9p_user_cred_ref(pfid->ucred); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RXATTRWALK); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, attrsize, u64); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RXATTRWALK: tag=%u fid=%u attrfid=%u name=%.*s size=%llu", (u32) *msgtag, *fid, *attrfid, *name_len, name_str, (unsigned long long)attrsize); return 1; } /* _9p_xattrwalk */
int _9p_statfs(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; struct _9p_fid *pfid = NULL; u32 type = 0x01021997; /* V9FS_MAGIC */ u32 bsize = 1; /* cache_inode_statfs and * FSAL already care for blocksize */ u64 *blocks = NULL; u64 *bfree = NULL; u64 *bavail = NULL; u64 *files = NULL; u64 *ffree = NULL; u64 fsid = 0LL; u32 namelen = MAXNAMLEN; fsal_dynamicfsinfo_t dynamicinfo; cache_inode_status_t cache_status; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TSTATFS: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; if (pfid == NULL) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); _9p_init_opctx(pfid, req9p); /* Get the FS's stats */ cache_status = cache_inode_statfs(pfid->pentry, &dynamicinfo); if (cache_status != CACHE_INODE_SUCCESS) return _9p_rerror(req9p, msgtag, _9p_tools_errno(cache_status), plenout, preply); blocks = (u64 *) &dynamicinfo.total_bytes; bfree = (u64 *) &dynamicinfo.free_bytes; bavail = (u64 *) &dynamicinfo.avail_bytes; files = (u64 *) &dynamicinfo.total_files; ffree = (u64 *) &dynamicinfo.free_files; fsid = (u64) pfid->pentry->obj_handle->attrs->rawdev.major; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RSTATFS); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, type, u32); _9p_setvalue(cursor, bsize, u32); _9p_setptr(cursor, blocks, u64); _9p_setptr(cursor, bfree, u64); _9p_setptr(cursor, bavail, u64); _9p_setptr(cursor, files, u64); _9p_setptr(cursor, ffree, u64); _9p_setvalue(cursor, fsid, u64); _9p_setvalue(cursor, namelen, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RSTATFS: tag=%u fid=%u", (u32) *msgtag, *fid); return 1; }
int _9p_symlink(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u16 *name_len = NULL; char *name_str = NULL; u16 *linkcontent_len = NULL; char *linkcontent_str = NULL; u32 *gid = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_symlink; cache_entry_t *pentry_symlink = NULL; char symlink_name[MAXNAMLEN]; uint64_t fileid; cache_inode_status_t cache_status; uint32_t mode = 0777; cache_inode_create_arg_t create_arg; memset(&create_arg, 0, sizeof(create_arg)); /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getstr(cursor, linkcontent_len, linkcontent_str); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TSYMLINK: tag=%u fid=%u name=%.*s linkcontent=%.*s gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *linkcontent_len, linkcontent_str, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); snprintf(symlink_name, MAXNAMLEN, "%.*s", *name_len, name_str); create_arg.link_content = gsh_malloc(MAXPATHLEN); if (create_arg.link_content == NULL) return _9p_rerror(req9p, msgtag, EFAULT, plenout, preply); snprintf(create_arg.link_content, MAXPATHLEN, "%.*s", *linkcontent_len, linkcontent_str); /* Let's do the job */ /* BUGAZOMEU: @todo : the gid parameter is not used yet, * flags is not yet used */ cache_status = cache_inode_create(pfid->pentry, symlink_name, SYMBOLIC_LINK, mode, &create_arg, &pentry_symlink); if (create_arg.link_content != NULL) gsh_free(create_arg.link_content); if (pentry_symlink == NULL) { return _9p_rerror(req9p, msgtag, _9p_tools_errno(cache_status), plenout, preply); } fileid = cache_inode_fileid(pentry_symlink); /* put the entry. */ cache_inode_put(pentry_symlink); /* Build the qid */ qid_symlink.type = _9P_QTSYMLINK; qid_symlink.version = 0; qid_symlink.path = fileid; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RSYMLINK); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_symlink); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RSYMLINK: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu)", (u32) *msgtag, *fid, *name_len, name_str, qid_symlink.type, qid_symlink.version, (unsigned long long)qid_symlink.path); return 1; }
int _9p_write(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u64 *offset = NULL; u32 *count = NULL; u32 outcount = 0; struct _9p_fid *pfid = NULL; size_t size; size_t written_size = 0; bool eof_met; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; /* bool sync = true; */ bool sync = false; char *databuffer = NULL; /* fsal_status_t fsal_status; */ /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, offset, u64); _9p_getptr(cursor, count, u32); databuffer = cursor; LogDebug(COMPONENT_9P, "TWRITE: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Make sure the requested amount of data respects negotiated msize */ if (*count + _9P_ROOM_TWRITE > req9p->pconn->msize) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* Do the job */ size = *count; if (pfid->specdata.xattr.xattr_content != NULL) { memcpy(pfid->specdata.xattr.xattr_content + (*offset), databuffer, size); pfid->specdata.xattr.xattr_offset += size; pfid->specdata.xattr.xattr_write = true; /* ADD CODE TO DETECT GAP */ #if 0 fsal_status = pfid->pentry->obj_handle->ops->setextattr_value_by_id( pfid->pentry->obj_handle, &pfid->op_context, pfid->specdata.xattr.xattr_id, xattrval, size + 1); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); #endif outcount = *count; } else { cache_status = cache_inode_rdwr(pfid->pentry, CACHE_INODE_WRITE, *offset, size, &written_size, databuffer, &eof_met, &sync, NULL); /* Get the handle, for stats */ struct gsh_client *client = req9p->pconn->client; if (client == NULL) { LogDebug(COMPONENT_9P, "Cannot get client block for 9P request"); } else { op_ctx->client = client; server_stats_io_done(size, written_size, (cache_status == CACHE_INODE_SUCCESS) ? true : false, true); } if (cache_status != CACHE_INODE_SUCCESS) return _9p_rerror(req9p, msgtag, _9p_tools_errno(cache_status), plenout, preply); outcount = (u32) written_size; } /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RWRITE); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, outcount, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RWRITE: tag=%u fid=%u offset=%llu input count=%u output count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count, outcount); /** * @todo write statistics accounting goes here * modeled on nfs I/O stats */ return 1; }
int _9p_unlinkat(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *dfid = NULL; u16 *name_len = NULL; char *name_str = NULL; /* flags are not used */ __attribute__ ((unused)) u32 *flags = NULL; struct _9p_fid *pdfid = NULL; fsal_status_t fsal_status; char name[MAXNAMLEN]; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, dfid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, flags, u32); LogDebug(COMPONENT_9P, "TUNLINKAT: tag=%u dfid=%u name=%.*s", (u32) *msgtag, *dfid, *name_len, name_str); if (*dfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pdfid = req9p->pconn->fids[*dfid]; /* Check that it is a valid fid */ if (pdfid == NULL || pdfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *dfid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pdfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* Let's do the job */ snprintf(name, MAXNAMLEN, "%.*s", *name_len, name_str); fsal_status = fsal_remove(pdfid->pentry, name); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RUNLINKAT); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "TUNLINKAT: tag=%u dfid=%u name=%.*s", (u32) *msgtag, *dfid, *name_len, name_str); return 1; }