int frontend_changed(vbd_t * const device, const XenbusState state) { int err = 0; DBG(device, "front-end went into state %s\n", xenbus_strstate(state)); device->frontend_state = state; switch (state) { case XenbusStateInitialising: if (device->hotplug_status_connected) err = xenbus_switch_state(device, XenbusStateInitWait); break; case XenbusStateInitialised: case XenbusStateConnected: /* * Already connected when the front-end switched to Initialising? */ if (device->hotplug_status_connected && device->state != XenbusStateConnected) err = connect(device); break; case XenbusStateClosing: err = xenbus_switch_state(device, XenbusStateClosing); break; case XenbusStateClosed: err = backend_close(device); break; default: err = EINVAL; WARN(device, "invalid front-end state %d", state); break; } return err; }
/* * close a file descriptor * returns error number from real close() if applicable */ int fd_close(int fd, int kind, int really_close) { int idx, res1 = 0, res2 = 0; idx = idx_by_fd(fd, kind); if (idx != -1) { /* update usage time of cache entry */ fd_cache[idx].use = time(NULL); if (really_close == FD_CLOSE_REAL) /* delete entry on real close, will close() fd */ return fd_cache_del(idx, FALSE); else return 0; } else { /* not in cache, sync and close directly */ if (kind == UNFS3_FD_WRITE) res1 = backend_fsync(fd); res2 = backend_close(fd); if (res1 != 0) return res1; else return res2; } }
/* * open a file descriptor * uses fd from cache if possible */ int fd_open(const char *path, nfs_fh3 nfh, int kind, int allow_caching) { int idx, res, fd; backend_statstruct buf; unfs3_fh_t *fh = (void *) nfh.data.data_val; idx = idx_by_fh(fh, kind); if (idx != -1) { if (fd_cache[idx].fd == -1) { /* pending error, report to client and remove from cache */ fd_cache_del(idx, FALSE); return -1; } return fd_cache[idx].fd; } else { /* call open to obtain new fd */ if (kind == UNFS3_FD_READ) fd = backend_open(path, O_RDONLY); else fd = backend_open(path, O_WRONLY); if (fd == -1) return -1; /* check for local fs race */ res = backend_fstat(fd, &buf); if ((res == -1) || (fh->dev != buf.st_dev || fh->ino != buf.st_ino || fh->gen != backend_get_gen(buf, fd, path))) { /* * local fs changed meaning of path between * calling NFS operation doing fh_decomp and * arriving here * * set errno to ELOOP to make calling NFS * operation return NFS3ERR_STALE */ backend_close(fd); errno = ELOOP; return -1; } /* * success, add to cache for later use */ if (allow_caching) fd_cache_add(fd, fh, kind); return fd; } }
/* * remove an entry from the cache. The keep_on_error variable * indicates if the entry should be kept in the cache upon * fsync/close failures. It should be set to TRUE when fd_cache_del is * called from a code path which cannot report an IO error back to the * client through WRITE or COMMIT. */ static int fd_cache_del(int idx, int keep_on_error) { int res1, res2; res1 = -1; if (fd_cache[idx].fd != -1) { if (fd_cache[idx].kind == UNFS3_FD_WRITE) { /* sync file data if writing descriptor */ fd_cache_writers--; res1 = backend_fsync(fd_cache[idx].fd); } else { fd_cache_readers--; res1 = 0; } res2 = backend_close(fd_cache[idx].fd); fd_cache[idx].fd = -1; /* return -1 if something went wrong during sync or close */ if (res1 == -1 || res2 == -1) { res1 = -1; } } else /* pending error */ errno = EIO; if (res1 == -1 && !keep_on_error) { /* The verifier should not be changed until we actually report & remove the error */ regenerate_write_verifier(); } if (res1 != -1 || !keep_on_error) { fd_cache[idx].fd = -1; fd_cache[idx].use = 0; fd_cache[idx].dev = 0; fd_cache[idx].ino = 0; fd_cache[idx].gen = 0; } return res1; }
CREATE3res *nfsproc3_create_3_svc(CREATE3args * argp, struct svc_req * rqstp) { static CREATE3res result; char *path; char obj[NFS_MAXPATHLEN]; sattr3 new_attr; int fd = -1, res = -1; backend_statstruct buf; uint32 gen; int flags = O_RDWR | O_CREAT | O_TRUNC | O_NONBLOCK; PREP(path, argp->where.dir); result.status = join(cat_name(path, argp->where.name, obj), exports_rw()); cluster_create(obj, rqstp, &result.status); /* GUARDED and EXCLUSIVE maps to Unix exclusive create */ if (argp->how.mode != UNCHECKED) flags = flags | O_EXCL; if (argp->how.mode != EXCLUSIVE) { new_attr = argp->how.createhow3_u.obj_attributes; result.status = join(result.status, atomic_attr(new_attr)); } /* Try to open the file */ if (result.status == NFS3_OK) { if (argp->how.mode != EXCLUSIVE) { fd = backend_open_create(obj, flags, create_mode(new_attr)); } else { fd = backend_open_create(obj, flags, create_mode(new_attr)); } } if (fd != -1) { /* Successful open */ res = backend_fstat(fd, &buf); if (res != -1) { /* Successful stat */ if (argp->how.mode == EXCLUSIVE) { /* Save verifier in atime and mtime */ res = backend_store_create_verifier(obj, argp->how.createhow3_u. verf); } } if (res != -1) { /* So far, so good */ gen = backend_get_gen(buf, fd, obj); fh_cache_add(buf.st_dev, buf.st_ino, obj); backend_close(fd); result.CREATE3res_u.resok.obj = fh_extend_post(argp->where.dir, buf.st_dev, buf.st_ino, gen); result.CREATE3res_u.resok.obj_attributes = get_post_buf(buf, rqstp); } if (res == -1) { /* backend_fstat() or backend_store_create_verifier() failed */ backend_close(fd); result.status = NFS3ERR_IO; } } else if (result.status == NFS3_OK) { /* open() failed */ if (argp->how.mode == EXCLUSIVE && errno == EEXIST) { /* Check if verifier matches */ fd = backend_open(obj, O_NONBLOCK); if (fd != -1) { res = backend_fstat(fd, &buf); } if (res != -1) { if (backend_check_create_verifier (&buf, argp->how.createhow3_u.verf)) { /* The verifier matched. Return success */ gen = backend_get_gen(buf, fd, obj); fh_cache_add(buf.st_dev, buf.st_ino, obj); backend_close(fd); result.CREATE3res_u.resok.obj = fh_extend_post(argp->where.dir, buf.st_dev, buf.st_ino, gen); result.CREATE3res_u.resok.obj_attributes = get_post_buf(buf, rqstp); } else { /* The verifier doesn't match */ result.status = NFS3ERR_EXIST; } } } if (res == -1) { result.status = create_err(); } } /* overlaps with resfail */ result.CREATE3res_u.resok.dir_wcc.before = get_pre_cached(); result.CREATE3res_u.resok.dir_wcc.after = get_post_stat(path, rqstp); return &result; }