/* * AFS read page from file (or symlink) */ static int afs_file_readpage(struct file *file, struct page *page) { struct afs_rxfs_fetch_descriptor desc; struct afs_vnode *vnode; struct inode *inode; int ret; inode = page->mapping->host; _enter("{%lu},%p{%lu}", inode->i_ino, page, page->index); vnode = AFS_FS_I(inode); BUG_ON(!PageLocked(page)); ret = -ESTALE; if (vnode->flags & AFS_VNODE_DELETED) goto error; #ifdef CONFIG_AFS_FSCACHE /* is it cached? */ ret = fscache_read_or_alloc_page(vnode->cache, page, afs_file_readpage_read_complete, NULL, GFP_KERNEL); #else ret = -ENOBUFS; #endif switch (ret) { /* read BIO submitted (page in cache) */ case 0: break; /* page not yet cached */ case -ENODATA: _debug("cache said ENODATA"); goto go_on; /* page will not be cached */ case -ENOBUFS: _debug("cache said ENOBUFS"); default: go_on: desc.fid = vnode->fid; desc.offset = page->index << PAGE_CACHE_SHIFT; desc.size = min((size_t) (inode->i_size - desc.offset), (size_t) PAGE_SIZE); desc.buffer = kmap(page); clear_page(desc.buffer); /* read the contents of the file from the server into the * page */ ret = afs_vnode_fetch_data(vnode, &desc); kunmap(page); if (ret < 0) { if (ret == -ENOENT) { kdebug("got NOENT from server" " - marking file deleted and stale"); vnode->flags |= AFS_VNODE_DELETED; ret = -ESTALE; } #ifdef CONFIG_AFS_FSCACHE fscache_uncache_page(vnode->cache, page); ClearPagePrivate(page); #endif goto error; } SetPageUptodate(page); /* send the page to the cache */ #ifdef CONFIG_AFS_FSCACHE if (PagePrivate(page)) { if (TestSetPageFsMisc(page)) BUG(); if (fscache_write_page(vnode->cache, page, afs_file_readpage_write_complete, NULL, GFP_KERNEL) != 0 ) { fscache_uncache_page(vnode->cache, page); ClearPagePrivate(page); end_page_fs_misc(page); } } #endif unlock_page(page); } _leave(" = 0"); return 0; error: SetPageError(page); unlock_page(page); _leave(" = %d", ret); return ret; } /* end afs_file_readpage() */
/* * generate a connection-level abort */ static int rxrpc_abort_connection(struct rxrpc_connection *conn, u32 error, u32 abort_code) { struct rxrpc_header hdr; struct msghdr msg; struct kvec iov[2]; __be32 word; size_t len; int ret; _enter("%d,,%u,%u", conn->debug_id, error, abort_code); /* generate a connection-level abort */ spin_lock_bh(&conn->state_lock); if (conn->state < RXRPC_CONN_REMOTELY_ABORTED) { conn->state = RXRPC_CONN_LOCALLY_ABORTED; conn->error = error; spin_unlock_bh(&conn->state_lock); } else { spin_unlock_bh(&conn->state_lock); _leave(" = 0 [already dead]"); return 0; } rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, abort_code); msg.msg_name = &conn->trans->peer->srx.transport.sin; msg.msg_namelen = sizeof(conn->trans->peer->srx.transport.sin); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; hdr.epoch = conn->epoch; hdr.cid = conn->cid; hdr.callNumber = 0; hdr.seq = 0; hdr.type = RXRPC_PACKET_TYPE_ABORT; hdr.flags = conn->out_clientflag; hdr.userStatus = 0; hdr.securityIndex = conn->security_ix; hdr._rsvd = 0; hdr.serviceId = conn->service_id; word = htonl(abort_code); iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); iov[1].iov_base = &word; iov[1].iov_len = sizeof(word); len = iov[0].iov_len + iov[1].iov_len; hdr.serial = htonl(atomic_inc_return(&conn->serial)); _proto("Tx CONN ABORT %%%u { %d }", ntohl(hdr.serial), abort_code); ret = kernel_sendmsg(conn->trans->local->socket, &msg, iov, 2, len); if (ret < 0) { _debug("sendmsg failed: %d", ret); return -EAGAIN; } _leave(" = 0"); return 0; }
/* * write to a file */ static int afs_store_data(struct address_space *mapping, pgoff_t first, pgoff_t last, unsigned offset, unsigned to) { struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct afs_fs_cursor fc; struct afs_wb_key *wbk = NULL; struct list_head *p; int ret = -ENOKEY, ret2; _enter("%s{%x:%u.%u},%lx,%lx,%x,%x", vnode->volume->name, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique, first, last, offset, to); spin_lock(&vnode->wb_lock); p = vnode->wb_keys.next; /* Iterate through the list looking for a valid key to use. */ try_next_key: while (p != &vnode->wb_keys) { wbk = list_entry(p, struct afs_wb_key, vnode_link); _debug("wbk %u", key_serial(wbk->key)); ret2 = key_validate(wbk->key); if (ret2 == 0) goto found_key; if (ret == -ENOKEY) ret = ret2; p = p->next; } spin_unlock(&vnode->wb_lock); afs_put_wb_key(wbk); _leave(" = %d [no keys]", ret); return ret; found_key: refcount_inc(&wbk->usage); spin_unlock(&vnode->wb_lock); _debug("USE WB KEY %u", key_serial(wbk->key)); ret = -ERESTARTSYS; if (afs_begin_vnode_operation(&fc, vnode, wbk->key)) { while (afs_select_fileserver(&fc)) { fc.cb_break = afs_calc_vnode_cb_break(vnode); afs_fs_store_data(&fc, mapping, first, last, offset, to); } afs_check_for_remote_deletion(&fc, fc.vnode); afs_vnode_commit_status(&fc, vnode, fc.cb_break); ret = afs_end_vnode_operation(&fc); } switch (ret) { case 0: afs_stat_v(vnode, n_stores); atomic_long_add((last * PAGE_SIZE + to) - (first * PAGE_SIZE + offset), &afs_v2net(vnode)->n_store_bytes); break; case -EACCES: case -EPERM: case -ENOKEY: case -EKEYEXPIRED: case -EKEYREJECTED: case -EKEYREVOKED: _debug("next"); spin_lock(&vnode->wb_lock); p = wbk->vnode_link.next; afs_put_wb_key(wbk); goto try_next_key; } afs_put_wb_key(wbk); _leave(" = %d", ret); return ret; }
/* * read page from file, directory or symlink, given a key to use */ int afs_page_filler(void *data, struct page *page) { struct inode *inode = page->mapping->host; struct afs_vnode *vnode = AFS_FS_I(inode); struct key *key = data; size_t len; off_t offset; int ret; _enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index); BUG_ON(!PageLocked(page)); ret = -ESTALE; if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) goto error; /* is it cached? */ #ifdef CONFIG_AFS_FSCACHE ret = fscache_read_or_alloc_page(vnode->cache, page, afs_file_readpage_read_complete, NULL, GFP_KERNEL); #else ret = -ENOBUFS; #endif switch (ret) { /* read BIO submitted (page in cache) */ case 0: break; /* page not yet cached */ case -ENODATA: _debug("cache said ENODATA"); goto go_on; /* page will not be cached */ case -ENOBUFS: _debug("cache said ENOBUFS"); default: go_on: offset = page->index << PAGE_CACHE_SHIFT; len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE); /* read the contents of the file from the server into the * page */ ret = afs_vnode_fetch_data(vnode, key, offset, len, page); if (ret < 0) { if (ret == -ENOENT) { _debug("got NOENT from server" " - marking file deleted and stale"); set_bit(AFS_VNODE_DELETED, &vnode->flags); ret = -ESTALE; } #ifdef CONFIG_AFS_FSCACHE fscache_uncache_page(vnode->cache, page); #endif BUG_ON(PageFsCache(page)); goto error; } SetPageUptodate(page); /* send the page to the cache */ #ifdef CONFIG_AFS_FSCACHE if (PageFsCache(page) && fscache_write_page(vnode->cache, page, GFP_KERNEL) != 0) { fscache_uncache_page(vnode->cache, page); BUG_ON(PageFsCache(page)); } #endif unlock_page(page); } _leave(" = 0"); return 0; error: SetPageError(page); unlock_page(page); _leave(" = %d", ret); return ret; }
/* * connection-level Rx packet processor */ static int rxrpc_process_event(struct rxrpc_connection *conn, struct sk_buff *skb, u32 *_abort_code) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); __be32 tmp; u32 serial; int loop, ret; if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) { kleave(" = -ECONNABORTED [%u]", conn->state); return -ECONNABORTED; } serial = ntohl(sp->hdr.serial); _enter("{%d},{%u,%%%u},", conn->debug_id, sp->hdr.type, serial); switch (sp->hdr.type) { case RXRPC_PACKET_TYPE_ABORT: if (skb_copy_bits(skb, 0, &tmp, sizeof(tmp)) < 0) return -EPROTO; _proto("Rx ABORT %%%u { ac=%d }", serial, ntohl(tmp)); conn->state = RXRPC_CONN_REMOTELY_ABORTED; rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, ntohl(tmp)); return -ECONNABORTED; case RXRPC_PACKET_TYPE_CHALLENGE: if (conn->security) return conn->security->respond_to_challenge( conn, skb, _abort_code); return -EPROTO; case RXRPC_PACKET_TYPE_RESPONSE: if (!conn->security) return -EPROTO; ret = conn->security->verify_response(conn, skb, _abort_code); if (ret < 0) return ret; ret = conn->security->init_connection_security(conn); if (ret < 0) return ret; conn->security->prime_packet_security(conn); read_lock_bh(&conn->lock); spin_lock(&conn->state_lock); if (conn->state == RXRPC_CONN_SERVER_CHALLENGING) { conn->state = RXRPC_CONN_SERVER; for (loop = 0; loop < RXRPC_MAXCALLS; loop++) rxrpc_call_is_secure(conn->channels[loop]); } spin_unlock(&conn->state_lock); read_unlock_bh(&conn->lock); return 0; default: _leave(" = -EPROTO [%u]", sp->hdr.type); return -EPROTO; } }
static int rxrpc_krb5_decode_principal(struct krb5_principal *princ, const __be32 **_xdr, unsigned *_toklen) { const __be32 *xdr = *_xdr; unsigned toklen = *_toklen, n_parts, loop, tmp; if (toklen <= 12) return -EINVAL; _enter(",{%x,%x,%x},%u", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), toklen); n_parts = ntohl(*xdr++); toklen -= 4; if (n_parts <= 0 || n_parts > AFSTOKEN_K5_COMPONENTS_MAX) return -EINVAL; princ->n_name_parts = n_parts; if (toklen <= (n_parts + 1) * 4) return -EINVAL; princ->name_parts = kcalloc(sizeof(char *), n_parts, GFP_KERNEL); if (!princ->name_parts) return -ENOMEM; for (loop = 0; loop < n_parts; loop++) { if (toklen < 4) return -EINVAL; tmp = ntohl(*xdr++); toklen -= 4; if (tmp <= 0 || tmp > AFSTOKEN_STRING_MAX) return -EINVAL; if (tmp > toklen) return -EINVAL; princ->name_parts[loop] = kmalloc(tmp + 1, GFP_KERNEL); if (!princ->name_parts[loop]) return -ENOMEM; memcpy(princ->name_parts[loop], xdr, tmp); princ->name_parts[loop][tmp] = 0; tmp = (tmp + 3) & ~3; toklen -= tmp; xdr += tmp >> 2; } if (toklen < 4) return -EINVAL; tmp = ntohl(*xdr++); toklen -= 4; if (tmp <= 0 || tmp > AFSTOKEN_K5_REALM_MAX) return -EINVAL; if (tmp > toklen) return -EINVAL; princ->realm = kmalloc(tmp + 1, GFP_KERNEL); if (!princ->realm) return -ENOMEM; memcpy(princ->realm, xdr, tmp); princ->realm[tmp] = 0; tmp = (tmp + 3) & ~3; toklen -= tmp; xdr += tmp >> 2; _debug("%s/...@%s", princ->name_parts[0], princ->realm); *_xdr = xdr; *_toklen = toklen; _leave(" = 0 [toklen=%u]", toklen); return 0; }
/* * Parse the source name to get cell name, volume name, volume type and R/W * selector. * * This can be one of the following: * "%[cell:]volume[.]" R/W volume * "#[cell:]volume[.]" R/O or R/W volume (R/O parent), * or R/W (R/W parent) volume * "%[cell:]volume.readonly" R/O volume * "#[cell:]volume.readonly" R/O volume * "%[cell:]volume.backup" Backup volume * "#[cell:]volume.backup" Backup volume */ static int afs_parse_source(struct fs_context *fc, struct fs_parameter *param) { struct afs_fs_context *ctx = fc->fs_private; struct afs_cell *cell; const char *cellname, *suffix, *name = param->string; int cellnamesz; _enter(",%s", name); if (!name) { printk(KERN_ERR "kAFS: no volume name specified\n"); return -EINVAL; } if ((name[0] != '%' && name[0] != '#') || !name[1]) { /* To use dynroot, we don't want to have to provide a source */ if (strcmp(name, "none") == 0) { ctx->no_cell = true; return 0; } printk(KERN_ERR "kAFS: unparsable volume name\n"); return -EINVAL; } /* determine the type of volume we're looking for */ if (name[0] == '%') { ctx->type = AFSVL_RWVOL; ctx->force = true; } name++; /* split the cell name out if there is one */ ctx->volname = strchr(name, ':'); if (ctx->volname) { cellname = name; cellnamesz = ctx->volname - name; ctx->volname++; } else { ctx->volname = name; cellname = NULL; cellnamesz = 0; } /* the volume type is further affected by a possible suffix */ suffix = strrchr(ctx->volname, '.'); if (suffix) { if (strcmp(suffix, ".readonly") == 0) { ctx->type = AFSVL_ROVOL; ctx->force = true; } else if (strcmp(suffix, ".backup") == 0) { ctx->type = AFSVL_BACKVOL; ctx->force = true; } else if (suffix[1] == 0) { } else { suffix = NULL; } } ctx->volnamesz = suffix ? suffix - ctx->volname : strlen(ctx->volname); _debug("cell %*.*s [%p]", cellnamesz, cellnamesz, cellname ?: "", ctx->cell); /* lookup the cell record */ if (cellname) { cell = afs_lookup_cell(ctx->net, cellname, cellnamesz, NULL, false); if (IS_ERR(cell)) { pr_err("kAFS: unable to lookup cell '%*.*s'\n", cellnamesz, cellnamesz, cellname ?: ""); return PTR_ERR(cell); } afs_put_cell(ctx->net, ctx->cell); ctx->cell = cell; } _debug("CELL:%s [%p] VOLUME:%*.*s SUFFIX:%s TYPE:%d%s", ctx->cell->name, ctx->cell, ctx->volnamesz, ctx->volnamesz, ctx->volname, suffix ?: "-", ctx->type, ctx->force ? " FORCE" : ""); fc->source = param->string; param->string = NULL; return 0; }
/* * deliver messages to a call */ static void afs_deliver_to_call(struct afs_call *call) { enum afs_call_state state; u32 abort_code, remote_abort = 0; int ret; _enter("%s", call->type->name); while (state = READ_ONCE(call->state), state == AFS_CALL_CL_AWAIT_REPLY || state == AFS_CALL_SV_AWAIT_OP_ID || state == AFS_CALL_SV_AWAIT_REQUEST || state == AFS_CALL_SV_AWAIT_ACK ) { if (state == AFS_CALL_SV_AWAIT_ACK) { size_t offset = 0; ret = rxrpc_kernel_recv_data(call->net->socket, call->rxcall, NULL, 0, &offset, false, &remote_abort, &call->service_id); trace_afs_recv_data(call, 0, offset, false, ret); if (ret == -EINPROGRESS || ret == -EAGAIN) return; if (ret < 0 || ret == 1) { if (ret == 1) ret = 0; goto call_complete; } return; } ret = call->type->deliver(call); state = READ_ONCE(call->state); switch (ret) { case 0: if (state == AFS_CALL_CL_PROC_REPLY) { if (call->cbi) set_bit(AFS_SERVER_FL_MAY_HAVE_CB, &call->cbi->server->flags); goto call_complete; } ASSERTCMP(state, >, AFS_CALL_CL_PROC_REPLY); goto done; case -EINPROGRESS: case -EAGAIN: goto out; case -EIO: case -ECONNABORTED: ASSERTCMP(state, ==, AFS_CALL_COMPLETE); goto done; case -ENOTSUPP: abort_code = RXGEN_OPCODE; rxrpc_kernel_abort_call(call->net->socket, call->rxcall, abort_code, ret, "KIV"); goto local_abort; case -ENODATA: case -EBADMSG: case -EMSGSIZE: default: abort_code = RXGEN_CC_UNMARSHAL; if (state != AFS_CALL_CL_AWAIT_REPLY) abort_code = RXGEN_SS_UNMARSHAL; rxrpc_kernel_abort_call(call->net->socket, call->rxcall, abort_code, -EBADMSG, "KUM"); goto local_abort; } } done: if (state == AFS_CALL_COMPLETE && call->incoming) afs_put_call(call); out: _leave(""); return; local_abort: abort_code = 0; call_complete: afs_set_call_complete(call, ret, remote_abort); state = AFS_CALL_COMPLETE; goto done; }
/* * wait synchronously for a call to complete */ static long afs_wait_for_call_to_complete(struct afs_call *call, struct afs_addr_cursor *ac) { signed long rtt2, timeout; long ret; u64 rtt; u32 life, last_life; DECLARE_WAITQUEUE(myself, current); _enter(""); rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall); rtt2 = nsecs_to_jiffies64(rtt) * 2; if (rtt2 < 2) rtt2 = 2; timeout = rtt2; last_life = rxrpc_kernel_check_life(call->net->socket, call->rxcall); add_wait_queue(&call->waitq, &myself); for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); /* deliver any messages that are in the queue */ if (!afs_check_call_state(call, AFS_CALL_COMPLETE) && call->need_attention) { call->need_attention = false; __set_current_state(TASK_RUNNING); afs_deliver_to_call(call); continue; } if (afs_check_call_state(call, AFS_CALL_COMPLETE)) break; life = rxrpc_kernel_check_life(call->net->socket, call->rxcall); if (timeout == 0 && life == last_life && signal_pending(current)) break; if (life != last_life) { timeout = rtt2; last_life = life; } timeout = schedule_timeout(timeout); } remove_wait_queue(&call->waitq, &myself); __set_current_state(TASK_RUNNING); /* Kill off the call if it's still live. */ if (!afs_check_call_state(call, AFS_CALL_COMPLETE)) { _debug("call interrupted"); if (rxrpc_kernel_abort_call(call->net->socket, call->rxcall, RX_USER_ABORT, -EINTR, "KWI")) afs_set_call_complete(call, -EINTR, 0); } spin_lock_bh(&call->state_lock); ac->abort_code = call->abort_code; ac->error = call->error; spin_unlock_bh(&call->state_lock); ret = ac->error; switch (ret) { case 0: if (call->ret_reply0) { ret = (long)call->reply[0]; call->reply[0] = NULL; } /* Fall through */ case -ECONNABORTED: ac->responded = true; break; } _debug("call complete"); afs_put_call(call); _leave(" = %p", (void *)ret); return ret; }
/* * set up a call for the given data * - called in process context with IRQs enabled */ struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *rx, struct rxrpc_transport *trans, struct rxrpc_conn_bundle *bundle, unsigned long user_call_ID, int create, gfp_t gfp) { struct rxrpc_call *call, *candidate; struct rb_node *p, *parent, **pp; _enter("%p,%d,%d,%lx,%d", rx, trans ? trans->debug_id : -1, bundle ? bundle->debug_id : -1, user_call_ID, create); /* search the extant calls first for one that matches the specified * user ID */ read_lock(&rx->call_lock); p = rx->calls.rb_node; while (p) { call = rb_entry(p, struct rxrpc_call, sock_node); if (user_call_ID < call->user_call_ID) p = p->rb_left; else if (user_call_ID > call->user_call_ID) p = p->rb_right; else goto found_extant_call; } read_unlock(&rx->call_lock); if (!create || !trans) return ERR_PTR(-EBADSLT); /* not yet present - create a candidate for a new record and then * redo the search */ candidate = rxrpc_alloc_client_call(rx, trans, bundle, gfp); if (IS_ERR(candidate)) { _leave(" = %ld", PTR_ERR(candidate)); return candidate; } candidate->user_call_ID = user_call_ID; __set_bit(RXRPC_CALL_HAS_USERID, &candidate->flags); write_lock(&rx->call_lock); pp = &rx->calls.rb_node; parent = NULL; while (*pp) { parent = *pp; call = rb_entry(parent, struct rxrpc_call, sock_node); if (user_call_ID < call->user_call_ID) pp = &(*pp)->rb_left; else if (user_call_ID > call->user_call_ID) pp = &(*pp)->rb_right; else goto found_extant_second; } /* second search also failed; add the new call */ call = candidate; candidate = NULL; rxrpc_get_call(call); rb_link_node(&call->sock_node, parent, pp); rb_insert_color(&call->sock_node, &rx->calls); write_unlock(&rx->call_lock); write_lock_bh(&rxrpc_call_lock); list_add_tail(&call->link, &rxrpc_calls); write_unlock_bh(&rxrpc_call_lock); _net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id); _leave(" = %p [new]", call); return call; /* we found the call in the list immediately */ found_extant_call: rxrpc_get_call(call); read_unlock(&rx->call_lock); _leave(" = %p [extant %d]", call, atomic_read(&call->usage)); return call; /* we found the call on the second time through the list */ found_extant_second: rxrpc_get_call(call); write_unlock(&rx->call_lock); rxrpc_put_call(candidate); _leave(" = %p [second %d]", call, atomic_read(&call->usage)); return call; }
/* * initiate a call */ long afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp, bool async) { struct sockaddr_rxrpc *srx = ac->addr; struct rxrpc_call *rxcall; struct msghdr msg; struct kvec iov[1]; size_t offset; s64 tx_total_len; int ret; _enter(",{%pISp},", &srx->transport); ASSERT(call->type != NULL); ASSERT(call->type->name != NULL); _debug("____MAKE %p{%s,%x} [%d]____", call, call->type->name, key_serial(call->key), atomic_read(&call->net->nr_outstanding_calls)); call->async = async; /* Work out the length we're going to transmit. This is awkward for * calls such as FS.StoreData where there's an extra injection of data * after the initial fixed part. */ tx_total_len = call->request_size; if (call->send_pages) { if (call->last == call->first) { tx_total_len += call->last_to - call->first_offset; } else { /* It looks mathematically like you should be able to * combine the following lines with the ones above, but * unsigned arithmetic is fun when it wraps... */ tx_total_len += PAGE_SIZE - call->first_offset; tx_total_len += call->last_to; tx_total_len += (call->last - call->first - 1) * PAGE_SIZE; } } /* create a call */ rxcall = rxrpc_kernel_begin_call(call->net->socket, srx, call->key, (unsigned long)call, tx_total_len, gfp, (async ? afs_wake_up_async_call : afs_wake_up_call_waiter), call->upgrade, call->debug_id); if (IS_ERR(rxcall)) { ret = PTR_ERR(rxcall); goto error_kill_call; } call->rxcall = rxcall; /* send the request */ iov[0].iov_base = call->request; iov[0].iov_len = call->request_size; msg.msg_name = NULL; msg.msg_namelen = 0; iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, iov, 1, call->request_size); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = MSG_WAITALL | (call->send_pages ? MSG_MORE : 0); ret = rxrpc_kernel_send_data(call->net->socket, rxcall, &msg, call->request_size, afs_notify_end_request_tx); if (ret < 0) goto error_do_abort; if (call->send_pages) { ret = afs_send_pages(call, &msg); if (ret < 0) goto error_do_abort; } /* at this point, an async call may no longer exist as it may have * already completed */ if (call->async) return -EINPROGRESS; return afs_wait_for_call_to_complete(call, ac); error_do_abort: call->state = AFS_CALL_COMPLETE; if (ret != -ECONNABORTED) { rxrpc_kernel_abort_call(call->net->socket, rxcall, RX_USER_ABORT, ret, "KSD"); } else { offset = 0; rxrpc_kernel_recv_data(call->net->socket, rxcall, NULL, 0, &offset, false, &call->abort_code, &call->service_id); ac->abort_code = call->abort_code; ac->responded = true; } call->error = ret; trace_afs_call_done(call); error_kill_call: afs_put_call(call); ac->error = ret; _leave(" = %d", ret); return ret; }
static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) { struct afs_super_info *super; struct vfsmount *mnt; struct page *page; size_t size; char *buf, *devname, *options; int ret; _enter("{%s}", mntpt->d_name.name); BUG_ON(!mntpt->d_inode); ret = -EINVAL; size = mntpt->d_inode->i_size; if (size > PAGE_SIZE - 1) goto error_no_devname; ret = -ENOMEM; devname = (char *) get_zeroed_page(GFP_KERNEL); if (!devname) goto error_no_devname; options = (char *) get_zeroed_page(GFP_KERNEL); if (!options) goto error_no_options; /* read the contents of the AFS special symlink */ page = read_mapping_page(mntpt->d_inode->i_mapping, 0, NULL); if (IS_ERR(page)) { ret = PTR_ERR(page); goto error_no_page; } ret = -EIO; if (PageError(page)) goto error; buf = kmap_atomic(page, KM_USER0); memcpy(devname, buf, size); kunmap_atomic(buf, KM_USER0); page_cache_release(page); page = NULL; /* work out what options we want */ super = AFS_FS_S(mntpt->d_sb); memcpy(options, "cell=", 5); strcpy(options + 5, super->volume->cell->name); if (super->volume->type == AFSVL_RWVOL) strcat(options, ",rwpath"); /* try and do the mount */ _debug("--- attempting mount %s -o %s ---", devname, options); mnt = vfs_kern_mount(&afs_fs_type, 0, devname, options); _debug("--- mount result %p ---", mnt); free_page((unsigned long) devname); free_page((unsigned long) options); _leave(" = %p", mnt); return mnt; error: page_cache_release(page); error_no_page: free_page((unsigned long) options); error_no_options: free_page((unsigned long) devname); error_no_devname: _leave(" = %d", ret); return ERR_PTR(ret); }
/* * iterate through the VL servers in a cell until one of them admits knowing * about the volume in question */ static int afs_vlocation_access_vl_by_id(struct afs_vlocation *vl, struct key *key, afs_volid_t volid, afs_voltype_t voltype, struct afs_cache_vlocation *vldb) { struct afs_cell *cell = vl->cell; struct in_addr addr; int count, ret; _enter("%s,%x,%d,", cell->name, volid, voltype); down_write(&vl->cell->vl_sem); ret = -ENOMEDIUM; for (count = cell->vl_naddrs; count > 0; count--) { addr = cell->vl_addrs[cell->vl_curr_svix]; _debug("CellServ[%hu]: %08x", cell->vl_curr_svix, addr.s_addr); /* attempt to access the VL server */ ret = afs_vl_get_entry_by_id(&addr, key, volid, voltype, vldb, &afs_sync_call); switch (ret) { case 0: goto out; case -ENOMEM: case -ENONET: case -ENETUNREACH: case -EHOSTUNREACH: case -ECONNREFUSED: if (ret == -ENOMEM || ret == -ENONET) goto out; goto rotate; case -EBUSY: vl->upd_busy_cnt++; if (vl->upd_busy_cnt <= 3) { if (vl->upd_busy_cnt > 1) { /* second+ BUSY - sleep a little bit */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); } continue; } break; case -ENOMEDIUM: vl->upd_rej_cnt++; goto rotate; default: ret = -EIO; goto rotate; } /* rotate the server records upon lookup failure */ rotate: cell->vl_curr_svix++; cell->vl_curr_svix %= cell->vl_naddrs; vl->upd_busy_cnt = 0; } out: if (ret < 0 && vl->upd_rej_cnt > 0) { printk(KERN_NOTICE "kAFS:" " Active volume no longer valid '%s'\n", vl->vldb.name); vl->valid = 0; ret = -ENOMEDIUM; } up_write(&vl->cell->vl_sem); _leave(" = %d", ret); return ret; }
/* * handle an error received on the local endpoint */ void rxrpc_UDP_error_report(struct sock *sk) { struct sock_exterr_skb *serr; struct rxrpc_transport *trans; struct rxrpc_local *local = sk->sk_user_data; struct rxrpc_peer *peer; struct sk_buff *skb; __be32 addr; __be16 port; _enter("%p{%d}", sk, local->debug_id); skb = sock_dequeue_err_skb(sk); if (!skb) { _leave("UDP socket errqueue empty"); return; } rxrpc_new_skb(skb); serr = SKB_EXT_ERR(skb); addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset); port = serr->port; _net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port)); _debug("Msg l:%d d:%d", skb->len, skb->data_len); peer = rxrpc_find_peer(local, addr, port); if (IS_ERR(peer)) { rxrpc_free_skb(skb); _leave(" [no peer]"); return; } trans = rxrpc_find_transport(local, peer); if (!trans) { rxrpc_put_peer(peer); rxrpc_free_skb(skb); _leave(" [no trans]"); return; } if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP && serr->ee.ee_type == ICMP_DEST_UNREACH && serr->ee.ee_code == ICMP_FRAG_NEEDED ) { u32 mtu = serr->ee.ee_info; _net("Rx Received ICMP Fragmentation Needed (%d)", mtu); /* wind down the local interface MTU */ if (mtu > 0 && peer->if_mtu == 65535 && mtu < peer->if_mtu) { peer->if_mtu = mtu; _net("I/F MTU %u", mtu); } if (mtu == 0) { /* they didn't give us a size, estimate one */ mtu = peer->if_mtu; if (mtu > 1500) { mtu >>= 1; if (mtu < 1500) mtu = 1500; } else {
/* * receive a message from an RxRPC socket * - we need to be careful about two or more threads calling recvmsg * simultaneously */ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct rxrpc_skb_priv *sp; struct rxrpc_call *call = NULL, *continue_call = NULL; struct rxrpc_sock *rx = rxrpc_sk(sock->sk); struct sk_buff *skb; long timeo; int copy, ret, ullen, offset, copied = 0; u32 abort_code; DEFINE_WAIT(wait); _enter(",,,%zu,%d", len, flags); if (flags & (MSG_OOB | MSG_TRUNC)) return -EOPNOTSUPP; ullen = msg->msg_flags & MSG_CMSG_COMPAT ? 4 : sizeof(unsigned long); timeo = sock_rcvtimeo(&rx->sk, flags & MSG_DONTWAIT); msg->msg_flags |= MSG_MORE; lock_sock(&rx->sk); for (;;) { /* return immediately if a client socket has no outstanding * calls */ if (RB_EMPTY_ROOT(&rx->calls)) { if (copied) goto out; if (rx->sk.sk_state != RXRPC_SERVER_LISTENING) { release_sock(&rx->sk); if (continue_call) rxrpc_put_call(continue_call); return -ENODATA; } } /* get the next message on the Rx queue */ skb = skb_peek(&rx->sk.sk_receive_queue); if (!skb) { /* nothing remains on the queue */ if (copied && (flags & MSG_PEEK || timeo == 0)) goto out; /* wait for a message to turn up */ release_sock(&rx->sk); prepare_to_wait_exclusive(sk_sleep(&rx->sk), &wait, TASK_INTERRUPTIBLE); ret = sock_error(&rx->sk); if (ret) goto wait_error; if (skb_queue_empty(&rx->sk.sk_receive_queue)) { if (signal_pending(current)) goto wait_interrupted; timeo = schedule_timeout(timeo); } finish_wait(sk_sleep(&rx->sk), &wait); lock_sock(&rx->sk); continue; } peek_next_packet: sp = rxrpc_skb(skb); call = sp->call; ASSERT(call != NULL); _debug("next pkt %s", rxrpc_pkts[sp->hdr.type]); /* make sure we wait for the state to be updated in this call */ spin_lock_bh(&call->lock); spin_unlock_bh(&call->lock); if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) { _debug("packet from released call"); if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) BUG(); rxrpc_free_skb(skb); continue; } /* determine whether to continue last data receive */ if (continue_call) { _debug("maybe cont"); if (call != continue_call || skb->mark != RXRPC_SKB_MARK_DATA) { release_sock(&rx->sk); rxrpc_put_call(continue_call); _leave(" = %d [noncont]", copied); return copied; } } rxrpc_get_call(call); /* copy the peer address and timestamp */ if (!continue_call) { if (msg->msg_name) { size_t len = sizeof(call->conn->trans->peer->srx); memcpy(msg->msg_name, &call->conn->trans->peer->srx, len); msg->msg_namelen = len; } sock_recv_ts_and_drops(msg, &rx->sk, skb); } /* receive the message */ if (skb->mark != RXRPC_SKB_MARK_DATA) goto receive_non_data_message; _debug("recvmsg DATA #%u { %d, %d }", ntohl(sp->hdr.seq), skb->len, sp->offset); if (!continue_call) { /* only set the control data once per recvmsg() */ ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID, ullen, &call->user_call_ID); if (ret < 0) goto copy_error; ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags)); } ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv); ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1); call->rx_data_recv = ntohl(sp->hdr.seq); ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten); offset = sp->offset; copy = skb->len - offset; if (copy > len - copied) copy = len - copied; if (skb->ip_summed == CHECKSUM_UNNECESSARY) { ret = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copy); } else { ret = skb_copy_and_csum_datagram_iovec(skb, offset, msg->msg_iov); if (ret == -EINVAL) goto csum_copy_error; } if (ret < 0) goto copy_error; /* handle piecemeal consumption of data packets */ _debug("copied %d+%d", copy, copied); offset += copy; copied += copy; if (!(flags & MSG_PEEK)) sp->offset = offset; if (sp->offset < skb->len) { _debug("buffer full"); ASSERTCMP(copied, ==, len); break; } /* we transferred the whole data packet */ if (sp->hdr.flags & RXRPC_LAST_PACKET) { _debug("last"); if (call->conn->out_clientflag) { /* last byte of reply received */ ret = copied; goto terminal_message; } /* last bit of request received */ if (!(flags & MSG_PEEK)) { _debug("eat packet"); if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) BUG(); rxrpc_free_skb(skb); } msg->msg_flags &= ~MSG_MORE; break; } /* move on to the next data message */ _debug("next"); if (!continue_call) continue_call = sp->call; else rxrpc_put_call(call); call = NULL; if (flags & MSG_PEEK) { _debug("peek next"); skb = skb->next; if (skb == (struct sk_buff *) &rx->sk.sk_receive_queue) break; goto peek_next_packet; } _debug("eat packet"); if (skb_dequeue(&rx->sk.sk_receive_queue) != skb) BUG(); rxrpc_free_skb(skb); }
/* * turn the raw key into something cooked * - the raw key should include the length in the two bytes at the front * - the key may be up to 514 bytes in length (including the length word) * - "base64" encode the strange keys, mapping 3 bytes of raw to four of * cooked * - need to cut the cooked key into 252 char lengths (189 raw bytes) */ char *cachefiles_cook_key(const u8 *raw, int keylen, uint8_t type) { unsigned char csum, ch; unsigned int acc; char *key; int loop, len, max, seg, mark, print; _enter(",%d", keylen); BUG_ON(keylen < 2 || keylen > 514); csum = raw[0] + raw[1]; print = 1; for (loop = 2; loop < keylen; loop++) { ch = raw[loop]; csum += ch; print &= cachefiles_filecharmap[ch]; } if (print) { /* if the path is usable ASCII, then we render it directly */ max = keylen - 2; max += 2; /* two base64'd length chars on the front */ max += 5; /* @checksum/M */ max += 3 * 2; /* maximum number of segment dividers (".../M") * is ((514 + 251) / 252) = 3 */ max += 1; /* NUL on end */ } else { /* calculate the maximum length of the cooked key */ keylen = (keylen + 2) / 3; max = keylen * 4; max += 5; /* @checksum/M */ max += 3 * 2; /* maximum number of segment dividers (".../M") * is ((514 + 188) / 189) = 3 */ max += 1; /* NUL on end */ } max += 1; /* 2nd NUL on end */ _debug("max: %d", max); key = kmalloc(max, GFP_KERNEL); if (!key) return NULL; len = 0; /* build the cooked key */ sprintf(key, "@%02x%c+", (unsigned) csum, 0); len = 5; mark = len - 1; if (print) { acc = *(uint16_t *) raw; raw += 2; key[len + 1] = cachefiles_charmap[acc & 63]; acc >>= 6; key[len] = cachefiles_charmap[acc & 63]; len += 2; seg = 250; for (loop = keylen; loop > 0; loop--) { if (seg <= 0) { key[len++] = '\0'; mark = len; key[len++] = '+'; seg = 252; } key[len++] = *raw++; ASSERT(len < max); } switch (type) { case FSCACHE_COOKIE_TYPE_INDEX: type = 'I'; break; case FSCACHE_COOKIE_TYPE_DATAFILE: type = 'D'; break; default: type = 'S'; break; } } else {
/* * add a cache */ static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache) { struct cachefiles_object *fsdef; struct path path; struct kstatfs stats; struct dentry *graveyard, *cachedir, *root; const struct cred *saved_cred; int ret; _enter(""); /* we want to work under the module's security ID */ ret = cachefiles_get_security_ID(cache); if (ret < 0) return ret; cachefiles_begin_secure(cache, &saved_cred); /* allocate the root index object */ ret = -ENOMEM; fsdef = kmem_cache_alloc(cachefiles_object_jar, GFP_KERNEL); if (!fsdef) goto error_root_object; ASSERTCMP(fsdef->backer, ==, NULL); atomic_set(&fsdef->usage, 1); fsdef->type = FSCACHE_COOKIE_TYPE_INDEX; _debug("- fsdef %p", fsdef); /* look up the directory at the root of the cache */ ret = kern_path(cache->rootdirname, LOOKUP_DIRECTORY, &path); if (ret < 0) goto error_open_root; cache->mnt = path.mnt; root = path.dentry; /* check parameters */ ret = -EOPNOTSUPP; if (!root->d_inode || !root->d_inode->i_op || !root->d_inode->i_op->lookup || !root->d_inode->i_op->mkdir || !root->d_inode->i_op->setxattr || !root->d_inode->i_op->getxattr || !root->d_sb->s_op->statfs || !root->d_sb->s_op->sync_fs) goto error_unsupported; ret = -EROFS; if (root->d_sb->s_flags & MS_RDONLY) goto error_unsupported; /* determine the security of the on-disk cache as this governs * security ID of files we create */ ret = cachefiles_determine_cache_security(cache, root, &saved_cred); if (ret < 0) goto error_unsupported; /* get the cache size and blocksize */ ret = vfs_statfs(&path, &stats); if (ret < 0) goto error_unsupported; ret = -ERANGE; if (stats.f_bsize <= 0) goto error_unsupported; ret = -EOPNOTSUPP; if (stats.f_bsize > PAGE_SIZE) goto error_unsupported; cache->bsize = stats.f_bsize; cache->bshift = 0; if (stats.f_bsize < PAGE_SIZE) cache->bshift = PAGE_SHIFT - ilog2(stats.f_bsize); _debug("blksize %u (shift %u)", cache->bsize, cache->bshift); _debug("size %llu, avail %llu", (unsigned long long) stats.f_blocks, (unsigned long long) stats.f_bavail); /* set up caching limits */ do_div(stats.f_files, 100); cache->fstop = stats.f_files * cache->fstop_percent; cache->fcull = stats.f_files * cache->fcull_percent; cache->frun = stats.f_files * cache->frun_percent; _debug("limits {%llu,%llu,%llu} files", (unsigned long long) cache->frun, (unsigned long long) cache->fcull, (unsigned long long) cache->fstop); stats.f_blocks >>= cache->bshift; do_div(stats.f_blocks, 100); cache->bstop = stats.f_blocks * cache->bstop_percent; cache->bcull = stats.f_blocks * cache->bcull_percent; cache->brun = stats.f_blocks * cache->brun_percent; _debug("limits {%llu,%llu,%llu} blocks", (unsigned long long) cache->brun, (unsigned long long) cache->bcull, (unsigned long long) cache->bstop); /* get the cache directory and check its type */ cachedir = cachefiles_get_directory(cache, root, "cache"); if (IS_ERR(cachedir)) { ret = PTR_ERR(cachedir); goto error_unsupported; } fsdef->dentry = cachedir; fsdef->fscache.cookie = NULL; ret = cachefiles_check_object_type(fsdef); if (ret < 0) goto error_unsupported; /* get the graveyard directory */ graveyard = cachefiles_get_directory(cache, root, "graveyard"); if (IS_ERR(graveyard)) { ret = PTR_ERR(graveyard); goto error_unsupported; } cache->graveyard = graveyard; /* publish the cache */ fscache_init_cache(&cache->cache, &cachefiles_cache_ops, "%s", fsdef->dentry->d_sb->s_id); fscache_object_init(&fsdef->fscache, NULL, &cache->cache); ret = fscache_add_cache(&cache->cache, &fsdef->fscache, cache->tag); if (ret < 0) goto error_add_cache; /* done */ set_bit(CACHEFILES_READY, &cache->flags); dput(root); printk(KERN_INFO "CacheFiles:" " File cache on %s registered\n", cache->cache.identifier); /* check how much space the cache has */ cachefiles_has_space(cache, 0, 0); cachefiles_end_secure(cache, saved_cred); return 0; error_add_cache: dput(cache->graveyard); cache->graveyard = NULL; error_unsupported: mntput(cache->mnt); cache->mnt = NULL; dput(fsdef->dentry); fsdef->dentry = NULL; dput(root); error_open_root: kmem_cache_free(cachefiles_object_jar, fsdef); error_root_object: cachefiles_end_secure(cache, saved_cred); kerror("Failed to register: %d", ret); return ret; }
/** * fscache_enqueue_operation - Enqueue an operation for processing * @op: The operation to enqueue * * Enqueue an operation for processing by the FS-Cache thread pool. * * This will get its own ref on the object. */ void fscache_enqueue_operation(struct fscache_operation *op) { _enter("{OBJ%x OP%x,%u}", op->object->debug_id, op->debug_id, atomic_read(&op->usage)); <<<<<<< HEAD
static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr, unsigned toklen) { struct rxrpc_key_token *token, **pptoken; size_t plen; u32 tktlen; int ret; _enter(",{%x,%x,%x,%x},%u", ntohl(xdr[0]), ntohl(xdr[1]), ntohl(xdr[2]), ntohl(xdr[3]), toklen); if (toklen <= 8 * 4) return -EKEYREJECTED; tktlen = ntohl(xdr[7]); _debug("tktlen: %x", tktlen); if (tktlen > AFSTOKEN_RK_TIX_MAX) return -EKEYREJECTED; if (8 * 4 + tktlen != toklen) return -EKEYREJECTED; plen = sizeof(*token) + sizeof(*token->kad) + tktlen; ret = key_payload_reserve(key, key->datalen + plen); if (ret < 0) return ret; plen -= sizeof(*token); token = kmalloc(sizeof(*token), GFP_KERNEL); if (!token) return -ENOMEM; token->kad = kmalloc(plen, GFP_KERNEL); if (!token->kad) { kfree(token); return -ENOMEM; } token->security_index = RXRPC_SECURITY_RXKAD; token->kad->ticket_len = tktlen; token->kad->vice_id = ntohl(xdr[0]); token->kad->kvno = ntohl(xdr[1]); token->kad->start = ntohl(xdr[4]); token->kad->expiry = ntohl(xdr[5]); token->kad->primary_flag = ntohl(xdr[6]); memcpy(&token->kad->session_key, &xdr[2], 8); memcpy(&token->kad->ticket, &xdr[8], tktlen); _debug("SCIX: %u", token->security_index); _debug("TLEN: %u", token->kad->ticket_len); _debug("EXPY: %x", token->kad->expiry); _debug("KVNO: %u", token->kad->kvno); _debug("PRIM: %u", token->kad->primary_flag); _debug("SKEY: %02x%02x%02x%02x%02x%02x%02x%02x", token->kad->session_key[0], token->kad->session_key[1], token->kad->session_key[2], token->kad->session_key[3], token->kad->session_key[4], token->kad->session_key[5], token->kad->session_key[6], token->kad->session_key[7]); if (token->kad->ticket_len >= 8) _debug("TCKT: %02x%02x%02x%02x%02x%02x%02x%02x", token->kad->ticket[0], token->kad->ticket[1], token->kad->ticket[2], token->kad->ticket[3], token->kad->ticket[4], token->kad->ticket[5], token->kad->ticket[6], token->kad->ticket[7]); key->type_data.x[0]++; for (pptoken = (struct rxrpc_key_token **)&key->payload.data; *pptoken; pptoken = &(*pptoken)->next) continue; *pptoken = token; if (token->kad->expiry < key->expiry) key->expiry = token->kad->expiry; _leave(" = 0"); return 0; }
/* * deal with one block in an AFS directory */ static int afs_dir_iterate_block(struct dir_context *ctx, union afs_dir_block *block, unsigned blkoff) { union afs_dirent *dire; unsigned offset, next, curr; size_t nlen; int tmp; _enter("%u,%x,%p,,",(unsigned)ctx->pos,blkoff,block); curr = (ctx->pos - blkoff) / sizeof(union afs_dirent); /* walk through the block, an entry at a time */ for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; offset < AFS_DIRENT_PER_BLOCK; offset = next ) { next = offset + 1; /* skip entries marked unused in the bitmap */ if (!(block->pagehdr.bitmap[offset / 8] & (1 << (offset % 8)))) { _debug("ENT[%Zu.%u]: unused", blkoff / sizeof(union afs_dir_block), offset); if (offset >= curr) ctx->pos = blkoff + next * sizeof(union afs_dirent); continue; } /* got a valid entry */ dire = &block->dirents[offset]; nlen = strnlen(dire->u.name, sizeof(*block) - offset * sizeof(union afs_dirent)); _debug("ENT[%Zu.%u]: %s %Zu \"%s\"", blkoff / sizeof(union afs_dir_block), offset, (offset < curr ? "skip" : "fill"), nlen, dire->u.name); /* work out where the next possible entry is */ for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) { if (next >= AFS_DIRENT_PER_BLOCK) { _debug("ENT[%Zu.%u]:" " %u travelled beyond end dir block" " (len %u/%Zu)", blkoff / sizeof(union afs_dir_block), offset, next, tmp, nlen); return -EIO; } if (!(block->pagehdr.bitmap[next / 8] & (1 << (next % 8)))) { _debug("ENT[%Zu.%u]:" " %u unmarked extension (len %u/%Zu)", blkoff / sizeof(union afs_dir_block), offset, next, tmp, nlen); return -EIO; } _debug("ENT[%Zu.%u]: ext %u/%Zu", blkoff / sizeof(union afs_dir_block), next, tmp, nlen); next++; } /* skip if starts before the current position */ if (offset < curr) continue; /* found the next entry */ if (!dir_emit(ctx, dire->u.name, nlen, ntohl(dire->u.vnode), ctx->actor == afs_lookup_filldir ? ntohl(dire->u.unique) : DT_UNKNOWN)) { _leave(" = 0 [full]"); return 0; } ctx->pos = blkoff + next * sizeof(union afs_dirent); } _leave(" = 1 [more]"); return 1; }
/* * fill in the superblock */ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx) { struct afs_super_info *as = AFS_FS_S(sb); struct afs_iget_data iget_data; struct inode *inode = NULL; int ret; _enter(""); /* fill in the superblock */ sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; sb->s_magic = AFS_FS_MAGIC; sb->s_op = &afs_super_ops; if (!as->dyn_root) sb->s_xattr = afs_xattr_handlers; ret = super_setup_bdi(sb); if (ret) return ret; sb->s_bdi->ra_pages = VM_READAHEAD_PAGES; /* allocate the root inode and dentry */ if (as->dyn_root) { inode = afs_iget_pseudo_dir(sb, true); sb->s_flags |= SB_RDONLY; } else { sprintf(sb->s_id, "%llu", as->volume->vid); afs_activate_volume(as->volume); iget_data.fid.vid = as->volume->vid; iget_data.fid.vnode = 1; iget_data.fid.vnode_hi = 0; iget_data.fid.unique = 1; iget_data.cb_v_break = as->volume->cb_v_break; iget_data.cb_s_break = 0; inode = afs_iget(sb, ctx->key, &iget_data, NULL, NULL, NULL); } if (IS_ERR(inode)) return PTR_ERR(inode); if (ctx->autocell || as->dyn_root) set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); ret = -ENOMEM; sb->s_root = d_make_root(inode); if (!sb->s_root) goto error; if (as->dyn_root) { sb->s_d_op = &afs_dynroot_dentry_operations; ret = afs_dynroot_populate(sb); if (ret < 0) goto error; } else { sb->s_d_op = &afs_fs_dentry_operations; } _leave(" = 0"); return 0; error: _leave(" = %d", ret); return ret; }
/* * do a lookup in a directory * - just returns the FID the dentry name maps to if found */ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, struct afs_fid *fid, struct key *key) { struct afs_super_info *as = dir->i_sb->s_fs_info; struct afs_lookup_cookie cookie = { .ctx.actor = afs_lookup_filldir, .name = dentry->d_name, .fid.vid = as->volume->vid }; int ret; _enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry); /* search the directory */ ret = afs_dir_iterate(dir, &cookie.ctx, key); if (ret < 0) { _leave(" = %d [iter]", ret); return ret; } ret = -ENOENT; if (!cookie.found) { _leave(" = -ENOENT [not found]"); return -ENOENT; } *fid = cookie.fid; _leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique); return 0; } /* * Try to auto mount the mountpoint with pseudo directory, if the autocell * operation is setted. */ static struct inode *afs_try_auto_mntpt( int ret, struct dentry *dentry, struct inode *dir, struct key *key, struct afs_fid *fid) { const char *devname = dentry->d_name.name; struct afs_vnode *vnode = AFS_FS_I(dir); struct inode *inode; _enter("%d, %p{%pd}, {%x:%u}, %p", ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key); if (ret != -ENOENT || !test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) goto out; inode = afs_iget_autocell(dir, devname, strlen(devname), key); if (IS_ERR(inode)) { ret = PTR_ERR(inode); goto out; } *fid = AFS_FS_I(inode)->fid; _leave("= %p", inode); return inode; out: _leave("= %d", ret); return ERR_PTR(ret); } /* * look up an entry in a directory */ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct afs_vnode *vnode; struct afs_fid fid; struct inode *inode; struct key *key; int ret; vnode = AFS_FS_I(dir); _enter("{%x:%u},%p{%pd},", vnode->fid.vid, vnode->fid.vnode, dentry, dentry); ASSERTCMP(d_inode(dentry), ==, NULL); if (dentry->d_name.len >= AFSNAMEMAX) { _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); } if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { _leave(" = -ESTALE"); return ERR_PTR(-ESTALE); } key = afs_request_key(vnode->volume->cell); if (IS_ERR(key)) { _leave(" = %ld [key]", PTR_ERR(key)); return ERR_CAST(key); } ret = afs_validate(vnode, key); if (ret < 0) { key_put(key); _leave(" = %d [val]", ret); return ERR_PTR(ret); } ret = afs_do_lookup(dir, dentry, &fid, key); if (ret < 0) { inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid); if (!IS_ERR(inode)) { key_put(key); goto success; } ret = PTR_ERR(inode); key_put(key); if (ret == -ENOENT) { d_add(dentry, NULL); _leave(" = NULL [negative]"); return NULL; } _leave(" = %d [do]", ret); return ERR_PTR(ret); } dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version; /* instantiate the dentry */ inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL); key_put(key); if (IS_ERR(inode)) { _leave(" = %ld", PTR_ERR(inode)); return ERR_CAST(inode); } success: d_add(dentry, inode); _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%u }", fid.vnode, fid.unique, d_inode(dentry)->i_ino, d_inode(dentry)->i_generation); return NULL; } /* * check that a dentry lookup hit has found a valid entry * - NOTE! the hit can be a negative hit too, so we can't assume we have an * inode */ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) { struct afs_vnode *vnode, *dir; struct afs_fid uninitialized_var(fid); struct dentry *parent; struct key *key; void *dir_version; int ret; if (flags & LOOKUP_RCU) return -ECHILD; vnode = AFS_FS_I(d_inode(dentry)); if (d_really_is_positive(dentry)) _enter("{v={%x:%u} n=%pd fl=%lx},", vnode->fid.vid, vnode->fid.vnode, dentry, vnode->flags); else _enter("{neg n=%pd}", dentry); key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell); if (IS_ERR(key)) key = NULL; /* lock down the parent dentry so we can peer at it */ parent = dget_parent(dentry); dir = AFS_FS_I(d_inode(parent)); /* validate the parent directory */ if (test_bit(AFS_VNODE_MODIFIED, &dir->flags)) afs_validate(dir, key); if (test_bit(AFS_VNODE_DELETED, &dir->flags)) { _debug("%pd: parent dir deleted", dentry); goto out_bad; } dir_version = (void *) (unsigned long) dir->status.data_version; if (dentry->d_fsdata == dir_version) goto out_valid; /* the dir contents are unchanged */ _debug("dir modified"); /* search the directory for this vnode */ ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key); switch (ret) { case 0: /* the filename maps to something */ if (d_really_is_negative(dentry)) goto out_bad; if (is_bad_inode(d_inode(dentry))) { printk("kAFS: afs_d_revalidate: %pd2 has bad inode\n", dentry); goto out_bad; } /* if the vnode ID has changed, then the dirent points to a * different file */ if (fid.vnode != vnode->fid.vnode) { _debug("%pd: dirent changed [%u != %u]", dentry, fid.vnode, vnode->fid.vnode); goto not_found; } /* if the vnode ID uniqifier has changed, then the file has * been deleted and replaced, and the original vnode ID has * been reused */ if (fid.unique != vnode->fid.unique) { _debug("%pd: file deleted (uq %u -> %u I:%u)", dentry, fid.unique, vnode->fid.unique, d_inode(dentry)->i_generation); spin_lock(&vnode->lock); set_bit(AFS_VNODE_DELETED, &vnode->flags); spin_unlock(&vnode->lock); goto not_found; } goto out_valid; case -ENOENT: /* the filename is unknown */ _debug("%pd: dirent not found", dentry); if (d_really_is_positive(dentry)) goto not_found; goto out_valid; default: _debug("failed to iterate dir %pd: %d", parent, ret); goto out_bad; } out_valid: dentry->d_fsdata = dir_version; dput(parent); key_put(key); _leave(" = 1 [valid]"); return 1; /* the dirent, if it exists, now points to a different vnode */ not_found: spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); out_bad: _debug("dropping dentry %pd2", dentry); dput(parent); key_put(key); _leave(" = 0 [bad]"); return 0; } /* * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't * sleep) * - called from dput() when d_count is going to 0. * - return 1 to request dentry be unhashed, 0 otherwise */ static int afs_d_delete(const struct dentry *dentry) { _enter("%pd", dentry); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto zap; if (d_really_is_positive(dentry) && (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(d_inode(dentry))->flags) || test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(d_inode(dentry))->flags))) goto zap; _leave(" = 0 [keep]"); return 0; zap: _leave(" = 1 [zap]"); return 1; }
/* * write back a dirty page */ static int afs_launder_page(struct page *page) { _enter("{%lu}", page->index); return 0; }
/* * handle dentry release */ static void afs_d_release(struct dentry *dentry) { _enter("%pd", dentry); }
/* * reject packets through the local endpoint */ void rxrpc_reject_packets(struct work_struct *work) { union { struct sockaddr sa; struct sockaddr_in sin; } sa; struct rxrpc_skb_priv *sp; struct rxrpc_header hdr; struct rxrpc_local *local; struct sk_buff *skb; struct msghdr msg; struct kvec iov[2]; size_t size; __be32 code; local = container_of(work, struct rxrpc_local, rejecter); rxrpc_get_local(local); _enter("%d", local->debug_id); iov[0].iov_base = &hdr; iov[0].iov_len = sizeof(hdr); iov[1].iov_base = &code; iov[1].iov_len = sizeof(code); size = sizeof(hdr) + sizeof(code); msg.msg_name = &sa; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; memset(&sa, 0, sizeof(sa)); sa.sa.sa_family = local->srx.transport.family; switch (sa.sa.sa_family) { case AF_INET: msg.msg_namelen = sizeof(sa.sin); break; default: msg.msg_namelen = 0; break; } memset(&hdr, 0, sizeof(hdr)); hdr.type = RXRPC_PACKET_TYPE_ABORT; while ((skb = skb_dequeue(&local->reject_queue))) { sp = rxrpc_skb(skb); switch (sa.sa.sa_family) { case AF_INET: sa.sin.sin_port = udp_hdr(skb)->source; sa.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; code = htonl(skb->priority); hdr.epoch = sp->hdr.epoch; hdr.cid = sp->hdr.cid; hdr.callNumber = sp->hdr.callNumber; hdr.serviceId = sp->hdr.serviceId; hdr.flags = sp->hdr.flags; hdr.flags ^= RXRPC_CLIENT_INITIATED; hdr.flags &= RXRPC_CLIENT_INITIATED; kernel_sendmsg(local->socket, &msg, iov, 2, size); break; default: break; } rxrpc_free_skb(skb); rxrpc_put_local(local); } rxrpc_put_local(local); _leave(""); }
/* * remove a file from an AFS filesystem */ static int afs_unlink(struct inode *dir, struct dentry *dentry) { struct afs_vnode *dvnode, *vnode; struct key *key; int ret; dvnode = AFS_FS_I(dir); _enter("{%x:%u},{%pd}", dvnode->fid.vid, dvnode->fid.vnode, dentry); ret = -ENAMETOOLONG; if (dentry->d_name.len >= AFSNAMEMAX) goto error; key = afs_request_key(dvnode->volume->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; } if (d_really_is_positive(dentry)) { vnode = AFS_FS_I(d_inode(dentry)); /* make sure we have a callback promise on the victim */ ret = afs_validate(vnode, key); if (ret < 0) goto error; } ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false); if (ret < 0) goto remove_error; if (d_really_is_positive(dentry)) { /* if the file wasn't deleted due to excess hard links, the * fileserver will break the callback promise on the file - if * it had one - before it returns to us, and if it was deleted, * it won't * * however, if we didn't have a callback promise outstanding, * or it was outstanding on a different server, then it won't * break it either... */ vnode = AFS_FS_I(d_inode(dentry)); if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) _debug("AFS_VNODE_DELETED"); if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) _debug("AFS_VNODE_CB_BROKEN"); set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); ret = afs_validate(vnode, key); _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); } key_put(key); _leave(" = 0"); return 0; remove_error: key_put(key); error: _leave(" = %d", ret); return ret; }
/* * mark a page as having been made dirty and thus needing writeback */ int afs_set_page_dirty(struct page *page) { _enter(""); return __set_page_dirty_nobuffers(page); }
/* * create a regular file on an AFS filesystem */ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct afs_file_status status; struct afs_callback cb; struct afs_server *server; struct afs_vnode *dvnode, *vnode; struct afs_fid fid; struct inode *inode; struct key *key; int ret; dvnode = AFS_FS_I(dir); _enter("{%x:%u},{%pd},%ho,", dvnode->fid.vid, dvnode->fid.vnode, dentry, mode); key = afs_request_key(dvnode->volume->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; } mode |= S_IFREG; ret = afs_vnode_create(dvnode, key, dentry->d_name.name, mode, &fid, &status, &cb, &server); if (ret < 0) goto create_error; inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); if (IS_ERR(inode)) { /* ENOMEM at a really inconvenient time - just abandon the new * directory on the server */ ret = PTR_ERR(inode); goto iget_error; } /* apply the status report we've got for the new vnode */ vnode = AFS_FS_I(inode); spin_lock(&vnode->lock); vnode->update_cnt++; spin_unlock(&vnode->lock); afs_vnode_finalise_status_update(vnode, server); afs_put_server(server); d_instantiate(dentry, inode); if (d_unhashed(dentry)) { _debug("not hashed"); d_rehash(dentry); } key_put(key); _leave(" = 0"); return 0; iget_error: afs_put_server(server); create_error: key_put(key); error: d_drop(dentry); _leave(" = %d", ret); return ret; }
/* * Synchronously write back the locked page and any subsequent non-locked dirty * pages. */ static int afs_write_back_from_locked_page(struct address_space *mapping, struct writeback_control *wbc, struct page *primary_page, pgoff_t final_page) { struct afs_vnode *vnode = AFS_FS_I(mapping->host); struct page *pages[8], *page; unsigned long count, priv; unsigned n, offset, to, f, t; pgoff_t start, first, last; int loop, ret; _enter(",%lx", primary_page->index); count = 1; if (test_set_page_writeback(primary_page)) BUG(); /* Find all consecutive lockable dirty pages that have contiguous * written regions, stopping when we find a page that is not * immediately lockable, is not dirty or is missing, or we reach the * end of the range. */ start = primary_page->index; priv = page_private(primary_page); offset = priv & AFS_PRIV_MAX; to = priv >> AFS_PRIV_SHIFT; trace_afs_page_dirty(vnode, tracepoint_string("store"), primary_page->index, priv); WARN_ON(offset == to); if (offset == to) trace_afs_page_dirty(vnode, tracepoint_string("WARN"), primary_page->index, priv); if (start >= final_page || (to < PAGE_SIZE && !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags))) goto no_more; start++; do { _debug("more %lx [%lx]", start, count); n = final_page - start + 1; if (n > ARRAY_SIZE(pages)) n = ARRAY_SIZE(pages); n = find_get_pages_contig(mapping, start, ARRAY_SIZE(pages), pages); _debug("fgpc %u", n); if (n == 0) goto no_more; if (pages[0]->index != start) { do { put_page(pages[--n]); } while (n > 0); goto no_more; } for (loop = 0; loop < n; loop++) { page = pages[loop]; if (to != PAGE_SIZE && !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)) break; if (page->index > final_page) break; if (!trylock_page(page)) break; if (!PageDirty(page) || PageWriteback(page)) { unlock_page(page); break; } priv = page_private(page); f = priv & AFS_PRIV_MAX; t = priv >> AFS_PRIV_SHIFT; if (f != 0 && !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)) { unlock_page(page); break; } to = t; trace_afs_page_dirty(vnode, tracepoint_string("store+"), page->index, priv); if (!clear_page_dirty_for_io(page)) BUG(); if (test_set_page_writeback(page)) BUG(); unlock_page(page); put_page(page); } count += loop; if (loop < n) { for (; loop < n; loop++) put_page(pages[loop]); goto no_more; } start += loop; } while (start <= final_page && count < 65536); no_more: /* We now have a contiguous set of dirty pages, each with writeback * set; the first page is still locked at this point, but all the rest * have been unlocked. */ unlock_page(primary_page); first = primary_page->index; last = first + count - 1; _debug("write back %lx[%u..] to %lx[..%u]", first, offset, last, to); ret = afs_store_data(mapping, first, last, offset, to); switch (ret) { case 0: ret = count; break; default: pr_notice("kAFS: Unexpected error from FS.StoreData %d\n", ret); /* Fall through */ case -EACCES: case -EPERM: case -ENOKEY: case -EKEYEXPIRED: case -EKEYREJECTED: case -EKEYREVOKED: afs_redirty_pages(wbc, mapping, first, last); mapping_set_error(mapping, ret); break; case -EDQUOT: case -ENOSPC: afs_redirty_pages(wbc, mapping, first, last); mapping_set_error(mapping, -ENOSPC); break; case -EROFS: case -EIO: case -EREMOTEIO: case -EFBIG: case -ENOENT: case -ENOMEDIUM: case -ENXIO: afs_kill_pages(mapping, first, last); mapping_set_error(mapping, ret); break; } _leave(" = %d", ret); return ret; }
/* * get bundle of client connections that a client socket can make use of */ struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *rx, struct rxrpc_transport *trans, struct key *key, __be16 service_id, gfp_t gfp) { struct rxrpc_conn_bundle *bundle, *candidate; struct rb_node *p, *parent, **pp; _enter("%p{%x},%x,%hx,", rx, key_serial(key), trans->debug_id, ntohs(service_id)); if (rx->trans == trans && rx->bundle) { atomic_inc(&rx->bundle->usage); return rx->bundle; } /* search the extant bundles first for one that matches the specified * user ID */ spin_lock(&trans->client_lock); p = trans->bundles.rb_node; while (p) { bundle = rb_entry(p, struct rxrpc_conn_bundle, node); if (rxrpc_cmp_bundle(bundle, key, service_id) < 0) p = p->rb_left; else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0) p = p->rb_right; else goto found_extant_bundle; } spin_unlock(&trans->client_lock); /* not yet present - create a candidate for a new record and then * redo the search */ candidate = rxrpc_alloc_bundle(gfp); if (!candidate) { _leave(" = -ENOMEM"); return ERR_PTR(-ENOMEM); } candidate->key = key_get(key); candidate->service_id = service_id; spin_lock(&trans->client_lock); pp = &trans->bundles.rb_node; parent = NULL; while (*pp) { parent = *pp; bundle = rb_entry(parent, struct rxrpc_conn_bundle, node); if (rxrpc_cmp_bundle(bundle, key, service_id) < 0) pp = &(*pp)->rb_left; else if (rxrpc_cmp_bundle(bundle, key, service_id) > 0) pp = &(*pp)->rb_right; else goto found_extant_second; } /* second search also failed; add the new bundle */ bundle = candidate; candidate = NULL; rb_link_node(&bundle->node, parent, pp); rb_insert_color(&bundle->node, &trans->bundles); spin_unlock(&trans->client_lock); _net("BUNDLE new on trans %d", trans->debug_id); if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { atomic_inc(&bundle->usage); rx->bundle = bundle; } _leave(" = %p [new]", bundle); return bundle; /* we found the bundle in the list immediately */ found_extant_bundle: atomic_inc(&bundle->usage); spin_unlock(&trans->client_lock); _net("BUNDLE old on trans %d", trans->debug_id); if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { atomic_inc(&bundle->usage); rx->bundle = bundle; } _leave(" = %p [extant %d]", bundle, atomic_read(&bundle->usage)); return bundle; /* we found the bundle on the second time through the list */ found_extant_second: atomic_inc(&bundle->usage); spin_unlock(&trans->client_lock); kfree(candidate); _net("BUNDLE old2 on trans %d", trans->debug_id); if (!rx->bundle && rx->sk.sk_state == RXRPC_CLIENT_CONNECTED) { atomic_inc(&bundle->usage); rx->bundle = bundle; } _leave(" = %p [second %d]", bundle, atomic_read(&bundle->usage)); return bundle; }