📄 fsclient.c
字号:
/* AFS File Server client stubs * * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/init.h>#include <linux/sched.h>#include <linux/circ_buf.h>#include "internal.h"#include "afs_fs.h"/* * decode an AFSFid block */static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid){ const __be32 *bp = *_bp; fid->vid = ntohl(*bp++); fid->vnode = ntohl(*bp++); fid->unique = ntohl(*bp++); *_bp = bp;}/* * decode an AFSFetchStatus block */static void xdr_decode_AFSFetchStatus(const __be32 **_bp, struct afs_file_status *status, struct afs_vnode *vnode, afs_dataversion_t *store_version){ afs_dataversion_t expected_version; const __be32 *bp = *_bp; umode_t mode; u64 data_version, size; u32 changed = 0; /* becomes non-zero if ctime-type changes seen */#define EXTRACT(DST) \ do { \ u32 x = ntohl(*bp++); \ changed |= DST - x; \ DST = x; \ } while (0) status->if_version = ntohl(*bp++); EXTRACT(status->type); EXTRACT(status->nlink); size = ntohl(*bp++); data_version = ntohl(*bp++); EXTRACT(status->author); EXTRACT(status->owner); EXTRACT(status->caller_access); /* call ticket dependent */ EXTRACT(status->anon_access); EXTRACT(status->mode); EXTRACT(status->parent.vnode); EXTRACT(status->parent.unique); bp++; /* seg size */ status->mtime_client = ntohl(*bp++); status->mtime_server = ntohl(*bp++); EXTRACT(status->group); bp++; /* sync counter */ data_version |= (u64) ntohl(*bp++) << 32; EXTRACT(status->lock_count); size |= (u64) ntohl(*bp++) << 32; bp++; /* spare 4 */ *_bp = bp; if (size != status->size) { status->size = size; changed |= true; } status->mode &= S_IALLUGO; _debug("vnode time %lx, %lx", status->mtime_client, status->mtime_server); if (vnode) { status->parent.vid = vnode->fid.vid; if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { _debug("vnode changed"); i_size_write(&vnode->vfs_inode, size); vnode->vfs_inode.i_uid = status->owner; vnode->vfs_inode.i_gid = status->group; vnode->vfs_inode.i_version = vnode->fid.unique; vnode->vfs_inode.i_nlink = status->nlink; mode = vnode->vfs_inode.i_mode; mode &= ~S_IALLUGO; mode |= status->mode; barrier(); vnode->vfs_inode.i_mode = mode; } vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server; vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; } expected_version = status->data_version; if (store_version) expected_version = *store_version; if (expected_version != data_version) { status->data_version = data_version; if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { _debug("vnode modified %llx on {%x:%u}", (unsigned long long) data_version, vnode->fid.vid, vnode->fid.vnode); set_bit(AFS_VNODE_MODIFIED, &vnode->flags); set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); } } else if (store_version) { status->data_version = data_version; }}/* * decode an AFSCallBack block */static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode){ const __be32 *bp = *_bp; vnode->cb_version = ntohl(*bp++); vnode->cb_expiry = ntohl(*bp++); vnode->cb_type = ntohl(*bp++); vnode->cb_expires = vnode->cb_expiry + get_seconds(); *_bp = bp;}static void xdr_decode_AFSCallBack_raw(const __be32 **_bp, struct afs_callback *cb){ const __be32 *bp = *_bp; cb->version = ntohl(*bp++); cb->expiry = ntohl(*bp++); cb->type = ntohl(*bp++); *_bp = bp;}/* * decode an AFSVolSync block */static void xdr_decode_AFSVolSync(const __be32 **_bp, struct afs_volsync *volsync){ const __be32 *bp = *_bp; volsync->creation = ntohl(*bp++); bp++; /* spare2 */ bp++; /* spare3 */ bp++; /* spare4 */ bp++; /* spare5 */ bp++; /* spare6 */ *_bp = bp;}/* * encode the requested attributes into an AFSStoreStatus block */static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr){ __be32 *bp = *_bp; u32 mask = 0, mtime = 0, owner = 0, group = 0, mode = 0; mask = 0; if (attr->ia_valid & ATTR_MTIME) { mask |= AFS_SET_MTIME; mtime = attr->ia_mtime.tv_sec; } if (attr->ia_valid & ATTR_UID) { mask |= AFS_SET_OWNER; owner = attr->ia_uid; } if (attr->ia_valid & ATTR_GID) { mask |= AFS_SET_GROUP; group = attr->ia_gid; } if (attr->ia_valid & ATTR_MODE) { mask |= AFS_SET_MODE; mode = attr->ia_mode & S_IALLUGO; } *bp++ = htonl(mask); *bp++ = htonl(mtime); *bp++ = htonl(owner); *bp++ = htonl(group); *bp++ = htonl(mode); *bp++ = 0; /* segment size */ *_bp = bp;}/* * decode an AFSFetchVolumeStatus block */static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp, struct afs_volume_status *vs){ const __be32 *bp = *_bp; vs->vid = ntohl(*bp++); vs->parent_id = ntohl(*bp++); vs->online = ntohl(*bp++); vs->in_service = ntohl(*bp++); vs->blessed = ntohl(*bp++); vs->needs_salvage = ntohl(*bp++); vs->type = ntohl(*bp++); vs->min_quota = ntohl(*bp++); vs->max_quota = ntohl(*bp++); vs->blocks_in_use = ntohl(*bp++); vs->part_blocks_avail = ntohl(*bp++); vs->part_max_blocks = ntohl(*bp++); *_bp = bp;}/* * deliver reply data to an FS.FetchStatus */static int afs_deliver_fs_fetch_status(struct afs_call *call, struct sk_buff *skb, bool last){ struct afs_vnode *vnode = call->reply; const __be32 *bp; _enter(",,%u", last); afs_transfer_reply(call, skb); if (!last) return 0; if (call->reply_size != call->reply_max) return -EBADMSG; /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); xdr_decode_AFSCallBack(&bp, vnode); if (call->reply2) xdr_decode_AFSVolSync(&bp, call->reply2); _leave(" = 0 [done]"); return 0;}/* * FS.FetchStatus operation type */static const struct afs_call_type afs_RXFSFetchStatus = { .name = "FS.FetchStatus", .deliver = afs_deliver_fs_fetch_status, .abort_to_error = afs_abort_to_error, .destructor = afs_flat_call_destructor,};/* * fetch the status information for a file */int afs_fs_fetch_file_status(struct afs_server *server, struct key *key, struct afs_vnode *vnode, struct afs_volsync *volsync, const struct afs_wait_mode *wait_mode){ struct afs_call *call; __be32 *bp; _enter(",%x,{%x:%u},,", key_serial(key), vnode->fid.vid, vnode->fid.vnode); call = afs_alloc_flat_call(&afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4); if (!call) return -ENOMEM; call->key = key; call->reply = vnode; call->reply2 = volsync; call->service_id = FS_SERVICE; call->port = htons(AFS_FS_PORT); /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHSTATUS); bp[1] = htonl(vnode->fid.vid); bp[2] = htonl(vnode->fid.vnode); bp[3] = htonl(vnode->fid.unique); return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);}/* * deliver reply data to an FS.FetchData */static int afs_deliver_fs_fetch_data(struct afs_call *call, struct sk_buff *skb, bool last){ struct afs_vnode *vnode = call->reply; const __be32 *bp; struct page *page; void *buffer; int ret; _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); switch (call->unmarshall) { case 0: call->offset = 0; call->unmarshall++; if (call->operation_ID != FSFETCHDATA64) { call->unmarshall++; goto no_msw; } /* extract the upper part of the returned data length of an * FSFETCHDATA64 op (which should always be 0 using this * client) */ case 1: _debug("extract data length (MSW)"); ret = afs_extract_data(call, skb, last, &call->tmp, 4); switch (ret) { case 0: break; case -EAGAIN: return 0; default: return ret; } call->count = ntohl(call->tmp); _debug("DATA length MSW: %u", call->count); if (call->count > 0) return -EBADMSG; call->offset = 0; call->unmarshall++; no_msw: /* extract the returned data length */ case 2: _debug("extract data length"); ret = afs_extract_data(call, skb, last, &call->tmp, 4); switch (ret) { case 0: break; case -EAGAIN: return 0; default: return ret; } call->count = ntohl(call->tmp); _debug("DATA length: %u", call->count); if (call->count > PAGE_SIZE) return -EBADMSG; call->offset = 0; call->unmarshall++; /* extract the returned data */ case 3: _debug("extract data"); if (call->count > 0) { page = call->reply3; buffer = kmap_atomic(page, KM_USER0); ret = afs_extract_data(call, skb, last, buffer, call->count); kunmap_atomic(buffer, KM_USER0); switch (ret) { case 0: break; case -EAGAIN: return 0; default: return ret; } } call->offset = 0; call->unmarshall++; /* extract the metadata */ case 4: ret = afs_extract_data(call, skb, last, call->buffer, (21 + 3 + 6) * 4); switch (ret) { case 0: break; case -EAGAIN: return 0; default: return ret; } bp = call->buffer; xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); xdr_decode_AFSCallBack(&bp, vnode); if (call->reply2) xdr_decode_AFSVolSync(&bp, call->reply2); call->offset = 0; call->unmarshall++; case 5: _debug("trailer"); if (skb->len != 0) return -EBADMSG; break; } if (!last) return 0; if (call->count < PAGE_SIZE) { _debug("clear"); page = call->reply3; buffer = kmap_atomic(page, KM_USER0); memset(buffer + call->count, 0, PAGE_SIZE - call->count); kunmap_atomic(buffer, KM_USER0); } _leave(" = 0 [done]"); return 0;}/* * FS.FetchData operation type */static const struct afs_call_type afs_RXFSFetchData = { .name = "FS.FetchData", .deliver = afs_deliver_fs_fetch_data, .abort_to_error = afs_abort_to_error, .destructor = afs_flat_call_destructor,};static const struct afs_call_type afs_RXFSFetchData64 = { .name = "FS.FetchData64", .deliver = afs_deliver_fs_fetch_data, .abort_to_error = afs_abort_to_error, .destructor = afs_flat_call_destructor,};/* * fetch data from a very large file */static int afs_fs_fetch_data64(struct afs_server *server, struct key *key, struct afs_vnode *vnode, off_t offset, size_t length, struct page *buffer, const struct afs_wait_mode *wait_mode){ struct afs_call *call; __be32 *bp; _enter(""); ASSERTCMP(length, <, ULONG_MAX); call = afs_alloc_flat_call(&afs_RXFSFetchData64, 32, (21 + 3 + 6) * 4); if (!call) return -ENOMEM; call->key = key; call->reply = vnode; call->reply2 = NULL; /* volsync */ call->reply3 = buffer; call->service_id = FS_SERVICE; call->port = htons(AFS_FS_PORT); call->operation_ID = FSFETCHDATA64; /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHDATA64); bp[1] = htonl(vnode->fid.vid); bp[2] = htonl(vnode->fid.vnode); bp[3] = htonl(vnode->fid.unique); bp[4] = htonl(upper_32_bits(offset)); bp[5] = htonl((u32) offset); bp[6] = 0; bp[7] = htonl((u32) length); return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);}/* * fetch data from a file */int afs_fs_fetch_data(struct afs_server *server, struct key *key, struct afs_vnode *vnode, off_t offset, size_t length, struct page *buffer, const struct afs_wait_mode *wait_mode){ struct afs_call *call; __be32 *bp; if (upper_32_bits(offset) || upper_32_bits(offset + length)) return afs_fs_fetch_data64(server, key, vnode, offset, length, buffer, wait_mode); _enter(""); call = afs_alloc_flat_call(&afs_RXFSFetchData, 24, (21 + 3 + 6) * 4); if (!call) return -ENOMEM; call->key = key; call->reply = vnode; call->reply2 = NULL; /* volsync */ call->reply3 = buffer; call->service_id = FS_SERVICE; call->port = htons(AFS_FS_PORT); call->operation_ID = FSFETCHDATA; /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHDATA); bp[1] = htonl(vnode->fid.vid); bp[2] = htonl(vnode->fid.vnode); bp[3] = htonl(vnode->fid.unique); bp[4] = htonl(offset); bp[5] = htonl(length); return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);}/* * deliver reply data to an FS.GiveUpCallBacks */static int afs_deliver_fs_give_up_callbacks(struct afs_call *call, struct sk_buff *skb, bool last){ _enter(",{%u},%d", skb->len, last); if (skb->len > 0) return -EBADMSG; /* shouldn't be any reply data */ return 0;}/* * FS.GiveUpCallBacks operation type */static const struct afs_call_type afs_RXFSGiveUpCallBacks = { .name = "FS.GiveUpCallBacks", .deliver = afs_deliver_fs_give_up_callbacks, .abort_to_error = afs_abort_to_error, .destructor = afs_flat_call_destructor,};/* * give up a set of callbacks * - the callbacks are held in the server->cb_break ring */int afs_fs_give_up_callbacks(struct afs_server *server, const struct afs_wait_mode *wait_mode){ struct afs_call *call; size_t ncallbacks; __be32 *bp, *tp; int loop; ncallbacks = CIRC_CNT(server->cb_break_head, server->cb_break_tail, ARRAY_SIZE(server->cb_break)); _enter("{%zu},", ncallbacks); if (ncallbacks == 0) return 0; if (ncallbacks > AFSCBMAX) ncallbacks = AFSCBMAX; _debug("break %zu callbacks", ncallbacks); call = afs_alloc_flat_call(&afs_RXFSGiveUpCallBacks, 12 + ncallbacks * 6 * 4, 0); if (!call) return -ENOMEM; call->service_id = FS_SERVICE; call->port = htons(AFS_FS_PORT); /* marshall the parameters */ bp = call->request; tp = bp + 2 + ncallbacks * 3; *bp++ = htonl(FSGIVEUPCALLBACKS); *bp++ = htonl(ncallbacks); *tp++ = htonl(ncallbacks); atomic_sub(ncallbacks, &server->cb_break_n); for (loop = ncallbacks; loop > 0; loop--) { struct afs_callback *cb = &server->cb_break[server->cb_break_tail]; *bp++ = htonl(cb->fid.vid); *bp++ = htonl(cb->fid.vnode); *bp++ = htonl(cb->fid.unique); *tp++ = htonl(cb->version); *tp++ = htonl(cb->expiry); *tp++ = htonl(cb->type); smp_mb(); server->cb_break_tail = (server->cb_break_tail + 1) & (ARRAY_SIZE(server->cb_break) - 1); } ASSERT(ncallbacks > 0); wake_up_nr(&server->cb_break_waitq, ncallbacks); return afs_make_call(&server->addr, call, GFP_NOFS, wait_mode);}/* * deliver reply data to an FS.CreateFile or an FS.MakeDir */static int afs_deliver_fs_create_vnode(struct afs_call *call, struct sk_buff *skb, bool last){ struct afs_vnode *vnode = call->reply; const __be32 *bp; _enter("{%u},{%u},%d", call->unmarshall, skb->len, last); afs_transfer_reply(call, skb); if (!last) return 0; if (call->reply_size != call->reply_max) return -EBADMSG; /* unmarshall the reply once we've received all of it */ bp = call->buffer; xdr_decode_AFSFid(&bp, call->reply2); xdr_decode_AFSFetchStatus(&bp, call->reply3, NULL, NULL); xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); xdr_decode_AFSCallBack_raw(&bp, call->reply4); /* xdr_decode_AFSVolSync(&bp, call->replyX); */ _leave(" = 0 [done]");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -