static void nfs4_setup_readdir(struct nfs4_compound *cp, u64 cookie, u32 *verifier, struct page **pages, unsigned int bufsize, struct dentry *dentry) { u32 *start, *p; struct nfs4_readdir *readdir = GET_OP(cp, readdir); BUG_ON(bufsize < 80); readdir->rd_cookie = (cookie > 2) ? cookie : 0; memcpy(&readdir->rd_req_verifier, verifier, sizeof(readdir->rd_req_verifier)); readdir->rd_count = bufsize; readdir->rd_bmval[0] = FATTR4_WORD0_FILEID; readdir->rd_bmval[1] = 0; readdir->rd_pages = pages; readdir->rd_pgbase = 0; OPNUM(cp) = OP_READDIR; cp->req_nops++; if (cookie >= 2) return; /* * NFSv4 servers do not return entries for '.' and '..' * Therefore, we fake these entries here. We let '.' * have cookie 0 and '..' have cookie 1. Note that * when talking to the server, we always send cookie 0 * instead of 1 or 2. */ start = p = (u32 *)kmap_atomic(*pages, KM_USER0); if (cookie == 0) { *p++ = xdr_one; /* next */ *p++ = xdr_zero; /* cookie, first word */ *p++ = xdr_one; /* cookie, second word */ *p++ = xdr_one; /* entry len */ memcpy(p, ".\0\0\0", 4); /* entry */ p++; *p++ = xdr_one; /* bitmap length */ *p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */ *p++ = htonl(8); /* attribute buffer length */ p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_inode)); } *p++ = xdr_one; /* next */ *p++ = xdr_zero; /* cookie, first word */ *p++ = xdr_two; /* cookie, second word */ *p++ = xdr_two; /* entry len */ memcpy(p, "..\0\0", 4); /* entry */ p++; *p++ = xdr_one; /* bitmap length */ *p++ = htonl(FATTR4_WORD0_FILEID); /* bitmap */ *p++ = htonl(8); /* attribute buffer length */ p = xdr_encode_hyper(p, NFS_FILEID(dentry->d_parent->d_inode)); readdir->rd_pgbase = (char *)p - (char *)start; readdir->rd_count -= readdir->rd_pgbase; kunmap_atomic(start, KM_USER0); }
/* * Handle a write reply that flushed part of a page. */ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; dprintk("NFS: %5u write(%s/%lld %d@%lld)", task->tk_pid, data->req->wb_context->path.dentry->d_inode->i_sb->s_id, (long long) NFS_FILEID(data->req->wb_context->path.dentry->d_inode), data->req->wb_bytes, (long long)req_offset(data->req)); nfs_writeback_done(task, data); }
/* * In NFSv3 we can have 64bit inode numbers. In order to support * this, and re-exported directories (also seen in NFSv2) * we are forced to allow 2 different inodes to have the same * i_ino. */ static int nfs_find_actor(struct inode *inode, void *opaque) { struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque; struct nfs_fh *fh = desc->fh; struct nfs_fattr *fattr = desc->fattr; if (NFS_FILEID(inode) != fattr->fileid) return 0; if (nfs_compare_fh(NFS_FH(inode), fh)) return 0; if (is_bad_inode(inode) || NFS_STALE(inode)) return 0; return 1; }
static void nfs_writeback_release_full(void *calldata) { struct nfs_write_data *data = calldata; int status = data->task.tk_status; /* Update attributes as result of writeback. */ while (!list_empty(&data->pages)) { struct nfs_page *req = nfs_list_entry(data->pages.next); struct page *page = req->wb_page; nfs_list_remove_request(req); dprintk("NFS: %5u write (%s/%lld %d@%lld)", data->task.tk_pid, req->wb_context->path.dentry->d_inode->i_sb->s_id, (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); if (status < 0) { nfs_set_pageerror(page); nfs_context_set_write_error(req->wb_context, status); dprintk(", error = %d\n", status); goto remove_request; } if (nfs_write_need_commit(data)) { memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); nfs_mark_request_commit(req); nfs_end_page_writeback(page); dprintk(" marked for commit\n"); goto next; } dprintk(" OK\n"); remove_request: nfs_end_page_writeback(page); nfs_inode_remove_request(req); next: nfs_clear_page_tag_locked(req); } nfs_writedata_release(calldata); }
/* * The "read_inode" function doesn't actually do anything: * the real data is filled in later in nfs_fhget. Here we * just mark the cache times invalid, and zero out i_mode * (the latter makes "nfs_refresh_inode" do the right thing * wrt pipe inodes) */ static void nfs_read_inode(struct inode * inode) { inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_rdev = 0; NFS_FILEID(inode) = 0; NFS_FSID(inode) = 0; INIT_LIST_HEAD(&inode->u.nfs_i.read); INIT_LIST_HEAD(&inode->u.nfs_i.dirty); INIT_LIST_HEAD(&inode->u.nfs_i.commit); INIT_LIST_HEAD(&inode->u.nfs_i.writeback); inode->u.nfs_i.nread = 0; inode->u.nfs_i.ndirty = 0; inode->u.nfs_i.ncommit = 0; inode->u.nfs_i.npages = 0; NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; }
/* * The "read_inode" function doesn't actually do anything: * the real data is filled in later in nfs_fhget. Here we * just mark the cache times invalid, and zero out i_mode * (the latter makes "nfs_refresh_inode" do the right thing * wrt pipe inodes) */ static void nfs_read_inode(struct inode * inode) { inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_rdev = 0; /* We can't support UPDATE_ATIME(), since the server will reset it */ inode->i_flags |= S_NOATIME; NFS_FILEID(inode) = 0; NFS_FSID(inode) = 0; NFS_FLAGS(inode) = 0; INIT_LIST_HEAD(&inode->u.nfs_i.read); INIT_LIST_HEAD(&inode->u.nfs_i.dirty); INIT_LIST_HEAD(&inode->u.nfs_i.commit); INIT_LIST_HEAD(&inode->u.nfs_i.writeback); inode->u.nfs_i.nread = 0; inode->u.nfs_i.ndirty = 0; inode->u.nfs_i.ncommit = 0; inode->u.nfs_i.npages = 0; NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; }
/** * nfs_sillyrename - Perform a silly-rename of a dentry * @dir: inode of directory that contains dentry * @dentry: dentry to be sillyrenamed * * NFSv2/3 is stateless and the server doesn't know when the client is * holding a file open. To prevent application problems when a file is * unlinked while it's still open, the client performs a "silly-rename". * That is, it renames the file to a hidden file in the same directory, * and only performs the unlink once the last reference to it is put. * * The final cleanup is done during dentry_iput. * * (Note: NFSv4 is stateful, and has opens, so in theory an NFSv4 server * could take responsibility for keeping open files referenced. The server * would also need to ensure that opened-but-deleted files were kept over * reboots. However, we may not assume a server does so. (RFC 5661 * does provide an OPEN4_RESULT_PRESERVE_UNLINKED flag that a server can * use to advertise that it does this; some day we may take advantage of * it.)) */ int nfs_sillyrename(struct inode *dir, struct dentry *dentry) { static unsigned int sillycounter; const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; const int countersize = sizeof(sillycounter)*2; const int slen = sizeof(".nfs")+fileidsize+countersize-1; char silly[slen+1]; struct dentry *sdentry; struct rpc_task *task; int error = -EIO; dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); nfs_inc_stats(dir, NFSIOS_SILLYRENAME); /* * We don't allow a dentry to be silly-renamed twice. */ error = -EBUSY; if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto out; sprintf(silly, ".nfs%*.*Lx", fileidsize, fileidsize, (unsigned long long)NFS_FILEID(dentry->d_inode)); /* Return delegation in anticipation of the rename */ NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode); sdentry = NULL; do { char *suffix = silly + slen - countersize; dput(sdentry); sillycounter++; sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); dfprintk(VFS, "NFS: trying to rename %s to %s\n", dentry->d_name.name, silly); sdentry = lookup_one_len(silly, dentry->d_parent, slen); /* * N.B. Better to return EBUSY here ... it could be * dangerous to delete the file while it's in use. */ if (IS_ERR(sdentry)) goto out; } while (sdentry->d_inode != NULL); /* need negative lookup */ /* queue unlink first. Can't do this from rpc_release as it * has to allocate memory */ error = nfs_async_unlink(dir, dentry); if (error) goto out_dput; /* populate unlinkdata with the right dname */ error = nfs_copy_dname(sdentry, (struct nfs_unlinkdata *)dentry->d_fsdata); if (error) { nfs_cancel_async_unlink(dentry); goto out_dput; } /* run the rename task, undo unlink if it fails */ task = nfs_async_rename(dir, dir, dentry, sdentry); if (IS_ERR(task)) { error = -EBUSY; nfs_cancel_async_unlink(dentry); goto out_dput; } /* wait for the RPC task to complete, unless a SIGKILL intervenes */ error = rpc_wait_for_completion_task(task); if (error == 0) error = task->tk_status; switch (error) { case 0: /* The rename succeeded */ nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); d_move(dentry, sdentry); break; case -ERESTARTSYS: /* The result of the rename is unknown. Play it safe by * forcing a new lookup */ d_drop(dentry); d_drop(sdentry); } rpc_put_task(task); out_dput: dput(sdentry); out: return error; }
/* * Set up the argument/result storage required for the RPC call. */ static int nfs_write_rpcsetup(struct nfs_page *req, struct nfs_write_data *data, const struct rpc_call_ops *call_ops, unsigned int count, unsigned int offset, int how) { struct inode *inode = req->wb_context->path.dentry->d_inode; int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; int priority = flush_task_priority(how); struct rpc_task *task; struct rpc_message msg = { .rpc_argp = &data->args, .rpc_resp = &data->res, .rpc_cred = req->wb_context->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = NFS_CLIENT(inode), .task = &data->task, .rpc_message = &msg, .callback_ops = call_ops, .callback_data = data, .workqueue = nfsiod_workqueue, .flags = flags, .priority = priority, }; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ data->req = req; data->inode = inode = req->wb_context->path.dentry->d_inode; data->cred = msg.rpc_cred; data->args.fh = NFS_FH(inode); data->args.offset = req_offset(req) + offset; data->args.pgbase = req->wb_pgbase + offset; data->args.pages = data->pagevec; data->args.count = count; data->args.context = get_nfs_open_context(req->wb_context); data->args.stable = NFS_UNSTABLE; if (how & FLUSH_STABLE) { data->args.stable = NFS_DATA_SYNC; if (!nfs_need_commit(NFS_I(inode))) data->args.stable = NFS_FILE_SYNC; } data->res.fattr = &data->fattr; data->res.count = count; data->res.verf = &data->verf; nfs_fattr_init(&data->fattr); /* Set up the initial task struct. */ NFS_PROTO(inode)->write_setup(data, &msg); dprintk("NFS: %5u initiated write call " "(req %s/%lld, %u bytes @ offset %llu)\n", data->task.tk_pid, inode->i_sb->s_id, (long long)NFS_FILEID(inode), count, (unsigned long long)data->args.offset); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); rpc_put_task(task); return 0; } /* If a nfs_flush_* function fails, it should remove reqs from @head and * call this on each, which will prepare them to be retried on next * writeback using standard nfs. */ static void nfs_redirty_request(struct nfs_page *req) { nfs_mark_request_dirty(req); nfs_end_page_writeback(req->wb_page); nfs_clear_page_tag_locked(req); } /* * Generate multiple small requests to write out a single * contiguous dirty area on one page. */ static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how) { struct nfs_page *req = nfs_list_entry(head->next); struct page *page = req->wb_page; struct nfs_write_data *data; size_t wsize = NFS_SERVER(inode)->wsize, nbytes; unsigned int offset; int requests = 0; int ret = 0; LIST_HEAD(list); nfs_list_remove_request(req); nbytes = count; do { size_t len = min(nbytes, wsize); data = nfs_writedata_alloc(1); if (!data) goto out_bad; list_add(&data->pages, &list); requests++; nbytes -= len; } while (nbytes != 0); atomic_set(&req->wb_complete, requests); ClearPageError(page); offset = 0; nbytes = count; do { int ret2; data = list_entry(list.next, struct nfs_write_data, pages); list_del_init(&data->pages); data->pagevec[0] = page; if (nbytes < wsize) wsize = nbytes; ret2 = nfs_write_rpcsetup(req, data, &nfs_write_partial_ops, wsize, offset, how); if (ret == 0) ret = ret2; offset += wsize; nbytes -= wsize; } while (nbytes != 0); return ret; out_bad: while (!list_empty(&list)) { data = list_entry(list.next, struct nfs_write_data, pages); list_del(&data->pages); nfs_writedata_release(data); } nfs_redirty_request(req); return -ENOMEM; } /* * Create an RPC task for the given write request and kick it. * The page must have been locked by the caller. * * It may happen that the page we're passed is not marked dirty. * This is the case if nfs_updatepage detects a conflicting request * that has been written but not committed. */ static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how) { struct nfs_page *req; struct page **pages; struct nfs_write_data *data; data = nfs_writedata_alloc(npages); if (!data) goto out_bad; pages = data->pagevec; while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_list_add_request(req, &data->pages); ClearPageError(req->wb_page); *pages++ = req->wb_page; } req = nfs_list_entry(data->pages.next); /* Set up the argument struct */ return nfs_write_rpcsetup(req, data, &nfs_write_full_ops, count, 0, how); out_bad: while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_redirty_request(req); } return -ENOMEM; } static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags) { size_t wsize = NFS_SERVER(inode)->wsize; if (wsize < PAGE_CACHE_SIZE) nfs_pageio_init(pgio, inode, nfs_flush_multi, wsize, ioflags); else nfs_pageio_init(pgio, inode, nfs_flush_one, wsize, ioflags); }
/* * Set up the argument/result storage required for the RPC call. */ static int nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data, int how) { struct nfs_page *first = nfs_list_entry(head->next); struct inode *inode = first->wb_context->path.dentry->d_inode; int flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; int priority = flush_task_priority(how); struct rpc_task *task; struct rpc_message msg = { .rpc_argp = &data->args, .rpc_resp = &data->res, .rpc_cred = first->wb_context->cred, }; struct rpc_task_setup task_setup_data = { .task = &data->task, .rpc_client = NFS_CLIENT(inode), .rpc_message = &msg, .callback_ops = &nfs_commit_ops, .callback_data = data, .workqueue = nfsiod_workqueue, .flags = flags, .priority = priority, }; /* Set up the RPC argument and reply structs * NB: take care not to mess about with data->commit et al. */ list_splice_init(head, &data->pages); data->inode = inode; data->cred = msg.rpc_cred; data->args.fh = NFS_FH(data->inode); /* Note: we always request a commit of the entire inode */ data->args.offset = 0; data->args.count = 0; data->args.context = get_nfs_open_context(first->wb_context); data->res.count = 0; data->res.fattr = &data->fattr; data->res.verf = &data->verf; nfs_fattr_init(&data->fattr); /* Set up the initial task struct. */ NFS_PROTO(inode)->commit_setup(data, &msg); dprintk("NFS: %5u initiated commit call\n", data->task.tk_pid); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) return PTR_ERR(task); rpc_put_task(task); return 0; } /* * Commit dirty pages */ static int nfs_commit_list(struct inode *inode, struct list_head *head, int how) { struct nfs_write_data *data; struct nfs_page *req; data = nfs_commitdata_alloc(); if (!data) goto out_bad; /* Set up the argument struct */ return nfs_commit_rpcsetup(head, data, how); out_bad: while (!list_empty(head)) { req = nfs_list_entry(head->next); nfs_list_remove_request(req); nfs_mark_request_commit(req); dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); dec_bdi_stat(req->wb_page->mapping->backing_dev_info, BDI_RECLAIMABLE); nfs_clear_page_tag_locked(req); } return -ENOMEM; } /* * COMMIT call returned */ static void nfs_commit_done(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; dprintk("NFS: %5u nfs_commit_done (status %d)\n", task->tk_pid, task->tk_status); /* Call the NFS version-specific code */ if (NFS_PROTO(data->inode)->commit_done(task, data) != 0) return; } static void nfs_commit_release(void *calldata) { struct nfs_write_data *data = calldata; struct nfs_page *req; int status = data->task.tk_status; while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); nfs_clear_request_commit(req); dprintk("NFS: commit (%s/%lld %d@%lld)", req->wb_context->path.dentry->d_inode->i_sb->s_id, (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), req->wb_bytes, (long long)req_offset(req)); if (status < 0) { nfs_context_set_write_error(req->wb_context, status); nfs_inode_remove_request(req); dprintk(", error = %d\n", status); goto next; } /* Okay, COMMIT succeeded, apparently. Check the verifier * returned by the server against all stored verfs. */ if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { /* We have a match */ nfs_inode_remove_request(req); dprintk(" OK\n"); goto next; } /* We have a mismatch. Write the page again */ dprintk(" mismatch\n"); nfs_mark_request_dirty(req); next: nfs_clear_page_tag_locked(req); } nfs_commitdata_release(calldata); } static const struct rpc_call_ops nfs_commit_ops = { #if defined(CONFIG_NFS_V4_1) .rpc_call_prepare = nfs_write_prepare, #endif /* CONFIG_NFS_V4_1 */ .rpc_call_done = nfs_commit_done, .rpc_release = nfs_commit_release, }; int nfs_commit_inode(struct inode *inode, int how) { LIST_HEAD(head); int res; spin_lock(&inode->i_lock); res = nfs_scan_commit(inode, &head, 0, 0); spin_unlock(&inode->i_lock); if (res) { int error = nfs_commit_list(inode, &head, how); if (error < 0) return error; } return res; } #else static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how) { return 0; } #endif long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how) { struct inode *inode = mapping->host; pgoff_t idx_start, idx_end; unsigned int npages = 0; LIST_HEAD(head); int nocommit = how & FLUSH_NOCOMMIT; long pages, ret; /* FIXME */ if (wbc->range_cyclic) idx_start = 0; else { idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; idx_end = wbc->range_end >> PAGE_CACHE_SHIFT; if (idx_end > idx_start) { pgoff_t l_npages = 1 + idx_end - idx_start; npages = l_npages; if (sizeof(npages) != sizeof(l_npages) && (pgoff_t)npages != l_npages) npages = 0; } } how &= ~FLUSH_NOCOMMIT; spin_lock(&inode->i_lock); do { ret = nfs_wait_on_requests_locked(inode, idx_start, npages); if (ret != 0) continue; if (nocommit) break; pages = nfs_scan_commit(inode, &head, idx_start, npages); if (pages == 0) break; if (how & FLUSH_INVALIDATE) { spin_unlock(&inode->i_lock); nfs_cancel_commit_list(&head); ret = pages; spin_lock(&inode->i_lock); continue; } pages += nfs_scan_commit(inode, &head, 0, 0); spin_unlock(&inode->i_lock); ret = nfs_commit_list(inode, &head, how); spin_lock(&inode->i_lock); } while (ret >= 0); spin_unlock(&inode->i_lock); return ret; } static int __nfs_write_mapping(struct address_space *mapping, struct writeback_control *wbc, int how) { int ret; ret = nfs_writepages(mapping, wbc); if (ret < 0) goto out; ret = nfs_sync_mapping_wait(mapping, wbc, how); if (ret < 0) goto out; return 0; out: __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); return ret; }
/* * Write a page synchronously. * Offset is the data offset within the page. */ static int nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, unsigned int offset, unsigned int count) { struct rpc_cred *cred = NULL; loff_t base; unsigned int wsize = NFS_SERVER(inode)->wsize; int result, refresh = 0, written = 0, flags; u8 *buffer; struct nfs_fattr fattr; struct nfs_writeverf verf; if (file) cred = get_rpccred(nfs_file_cred(file)); if (!cred) cred = get_rpccred(NFS_I(inode)->mm_cred); dprintk("NFS: nfs_writepage_sync(%x/%Ld %d@%Ld)\n", inode->i_dev, (long long)NFS_FILEID(inode), count, (long long)(page_offset(page) + offset)); buffer = kmap(page) + offset; base = page_offset(page) + offset; flags = ((IS_SWAPFILE(inode)) ? NFS_RW_SWAP : 0) | NFS_RW_SYNC; do { if (count < wsize && !IS_SWAPFILE(inode)) wsize = count; result = NFS_PROTO(inode)->write(inode, cred, &fattr, flags, base, wsize, buffer, &verf); nfs_write_attributes(inode, &fattr); if (result < 0) { /* Must mark the page invalid after I/O error */ ClearPageUptodate(page); goto io_error; } if (result != wsize) printk("NFS: short write, wsize=%u, result=%d\n", wsize, result); refresh = 1; buffer += wsize; base += wsize; written += wsize; count -= wsize; /* * If we've extended the file, update the inode * now so we don't invalidate the cache. */ if (base > inode->i_size) inode->i_size = base; } while (count); if (PageError(page)) ClearPageError(page); io_error: kunmap(page); if (cred) put_rpccred(cred); return written? written : result; }
int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr, const struct rpc_call_ops *call_ops, int how, int flags) { struct rpc_task *task; struct rpc_message msg = { .rpc_argp = &hdr->args, .rpc_resp = &hdr->res, .rpc_cred = hdr->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = clnt, .task = &hdr->task, .rpc_message = &msg, .callback_ops = call_ops, .callback_data = hdr, .workqueue = nfsiod_workqueue, .flags = RPC_TASK_ASYNC | flags, }; int ret = 0; hdr->rw_ops->rw_initiate(hdr, &msg, &task_setup_data, how); dprintk("NFS: %5u initiated pgio call " "(req %s/%llu, %u bytes @ offset %llu)\n", hdr->task.tk_pid, hdr->inode->i_sb->s_id, (unsigned long long)NFS_FILEID(hdr->inode), hdr->args.count, (unsigned long long)hdr->args.offset); task = rpc_run_task(&task_setup_data); if (IS_ERR(task)) { ret = PTR_ERR(task); goto out; } if (how & FLUSH_SYNC) { ret = rpc_wait_for_completion_task(task); if (ret == 0) ret = task->tk_status; } rpc_put_task(task); out: return ret; } EXPORT_SYMBOL_GPL(nfs_initiate_pgio); /** * nfs_pgio_error - Clean up from a pageio error * @desc: IO descriptor * @hdr: pageio header */ static int nfs_pgio_error(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr) { set_bit(NFS_IOHDR_REDO, &hdr->flags); nfs_pgio_data_destroy(hdr); hdr->completion_ops->completion(hdr); desc->pg_completion_ops->error_cleanup(&desc->pg_list); return -ENOMEM; } /** * nfs_pgio_release - Release pageio data * @calldata: The pageio header to release */ static void nfs_pgio_release(void *calldata) { struct nfs_pgio_header *hdr = calldata; if (hdr->rw_ops->rw_release) hdr->rw_ops->rw_release(hdr); nfs_pgio_data_destroy(hdr); hdr->completion_ops->completion(hdr); } /** * nfs_pageio_init - initialise a page io descriptor * @desc: pointer to descriptor * @inode: pointer to inode * @doio: pointer to io function * @bsize: io block size * @io_flags: extra parameters for the io function */ void nfs_pageio_init(struct nfs_pageio_descriptor *desc, struct inode *inode, const struct nfs_pageio_ops *pg_ops, const struct nfs_pgio_completion_ops *compl_ops, const struct nfs_rw_ops *rw_ops, size_t bsize, int io_flags) { INIT_LIST_HEAD(&desc->pg_list); desc->pg_bytes_written = 0; desc->pg_count = 0; desc->pg_bsize = bsize; desc->pg_base = 0; desc->pg_moreio = 0; desc->pg_recoalesce = 0; desc->pg_inode = inode; desc->pg_ops = pg_ops; desc->pg_completion_ops = compl_ops; desc->pg_rw_ops = rw_ops; desc->pg_ioflags = io_flags; desc->pg_error = 0; desc->pg_lseg = NULL; desc->pg_dreq = NULL; desc->pg_layout_private = NULL; }
int nfs_sillyrename(struct inode *dir, struct dentry *dentry) { static unsigned int sillycounter; const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; const int countersize = sizeof(sillycounter)*2; const int slen = sizeof(".nfs")+fileidsize+countersize-1; char silly[slen+1]; struct dentry *sdentry; struct rpc_task *task; int error = -EIO; dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); nfs_inc_stats(dir, NFSIOS_SILLYRENAME); error = -EBUSY; if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto out; sprintf(silly, ".nfs%*.*Lx", fileidsize, fileidsize, (unsigned long long)NFS_FILEID(dentry->d_inode)); nfs_inode_return_delegation(dentry->d_inode); sdentry = NULL; do { char *suffix = silly + slen - countersize; dput(sdentry); sillycounter++; sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); dfprintk(VFS, "NFS: trying to rename %s to %s\n", dentry->d_name.name, silly); sdentry = lookup_one_len(silly, dentry->d_parent, slen); if (IS_ERR(sdentry)) goto out; } while (sdentry->d_inode != NULL); error = nfs_async_unlink(dir, dentry); if (error) goto out_dput; error = nfs_copy_dname(sdentry, (struct nfs_unlinkdata *)dentry->d_fsdata); if (error) { nfs_cancel_async_unlink(dentry); goto out_dput; } task = nfs_async_rename(dir, dir, dentry, sdentry); if (IS_ERR(task)) { error = -EBUSY; nfs_cancel_async_unlink(dentry); goto out_dput; } error = rpc_wait_for_completion_task(task); if (error == 0) error = task->tk_status; switch (error) { case 0: /* The rename succeeded */ nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); d_move(dentry, sdentry); break; case -ERESTARTSYS: /* The result of the rename is unknown. Play it safe by * forcing a new lookup */ d_drop(dentry); d_drop(sdentry); } rpc_put_task(task); out_dput: dput(sdentry); out: return error; }