1.OPEN請求報文和應答報文中的數據
OPEN操作比較複雜,涉及到很多種特殊情況。按照RFC3530的規定,OPEN請求報文中需要包含下列數據:
struct OPEN4args {
seqid4 seqid; // 這是一個序號,保證請求能夠序列化.
uint32_t share_access; // 這是用戶請求的訪問權限
uint32_t share_deny; // 一定不能有這些權限,Linux中固定設置爲0.
open_owner4 owner; // clientid + 這次OPEN操作的名稱.
openflag4 openhow; // 有兩個取值:OPEN4_NOCREATE和OPEN4_CREATE
open_claim4 claim; // 有四個取值:CLAIM_NULL、CLAIM_PREVIOUS、CLAIM_DELEGATE_CUR、CLAIM_DELEGATE_PREV
};
owner包含兩個字段:clientid和這次操作的名稱, 服務器端可以根據clientid和OPEN操作的名稱查找是否已經保存了這次操作的信息。seqid是OPEN操作的序號,保證多個OPEN操作序列化執行。share_access是用戶打開文件時請求的訪問權限,share_deny是爲Windows share reservation設置的字段,Linux系統中這個字段固定爲0。openhow表示如果文件不存在是否創建一個新文件。claim表示OPEN類型,按照按照claim值不同,OPEN操作分爲四種類型。這篇文>章中,我們只講解最常見的一種情況:打開服務器端存在的一個文件。這種請求中claim的類型是CLAIM_NULL。這種情況下,open_claim4中還包含了文件的名>稱,這是我們要打開的文件。
按照RFC3530的規定,OPEN操作應答報文包含下列內容
struct OPEN4resok {
stateid4 stateid; /* Stateid for open */
change_info4 cinfo; /* Directory Change Info */
uint32_t rflags; /* Result flags */
bitmap4 attrset; /* attributes on create */
open_delegation4 delegation; /* Info on any open
delegation */
};
stateid固定長度爲16字節,代表了這個打開的文件,後續發起READ、WRITE操作時需要將這個stateid包含在請求報文中,服務器根據stateid查找對應的>文件。cinfo包含了父目錄的屬性信息。rflags是一些標誌位,目前定義了兩個標誌位: const OPEN4_RESULT_CONFIRM = 0x00000002; // 應答報文中的stateid不能馬上使用,需要發起OPEN_CONFIRM請求。
const OPEN4_RESULT_LOCKTYPE_POSIX = 0x00000004; // 服務器支持POSIX文件鎖
如果OPEN請求報文中openhow字段的值爲NFS4_CREATE,則服務器端可以創建一個新的文件,應答消息中的attrset就包含了新創建文件的屬性信息。應答消息最後一個字段是delegation的信息,RFC3530定義了三種取值:
OPEN_DELEGATE_NONE // 沒有使用delegation
OPEN_DELEGATE_READ // 讀操作使用的delegation
OPEN_DELEGATE_WRITE // 寫操作使用的delegation
OPEN_DELEGATE_READ表示讀操作中使用的delegation。如果文件以只讀方式打開,則可以申請這種delegation。一個客戶端的多個用戶或者不同的客戶端可以共享同一個OPEN_DELEGATE_READ,因爲多個用戶同時執行讀操作不會造成衝突。當另外一個用戶試圖向文件中寫入數據時服務器會回收OPEN_DELEGATE_READ。OPEN_DELEGATE_WRITE表示寫操作中使用的delegation,OPEN_DELEGATE_WRITE只能被一個用戶獨佔。當另一個用戶向文件寫入數據時服務器會回收OPEN_DELEGATE_WRITE。具體實現中,Linux沒有實現OPEN_DELEGATE_WRITE。
2.客戶端程序
客戶端設置OPEN請求報文中數據的函數是nfs4_opendata_alloc(),這個函數的代碼如下:
參數dentry:這是要打開的文件在客戶端的目錄項結構
參數sp:這表示客戶端的一個用戶,就是這個用戶要打開文件
參數fmode:這是文件的打開權限,讀權限還是寫權限
參數flags:這是打開文件時指定的一些標誌位,如O_CREAT表示如何文件不存在就創建一個新文件
參數attrs:這是爲新創建的文件設置的屬性信息,如訪問權限。
參數gfp_mask:這是分配內存時使用的標誌位
static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
struct nfs4_state_owner *sp, fmode_t fmode, int flags,
const struct iattr *attrs,
gfp_t gfp_mask)
{
struct dentry *parent = dget_parent(dentry); // 獲取父目錄的目錄項結構
struct inode *dir = parent->d_inode; // 這是父目錄的索引節點
struct nfs_server *server = NFS_SERVER(dir); // 文件系統結構
struct nfs4_opendata *p;
p = kzalloc(sizeof(*p), gfp_mask); // 分配內存
if (p == NULL)
goto err; // 分配內存失敗
// 這時分配了一個nfs_seqid結構,暫時沒有鏈接到nfs_seqid_counter中
p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
if (p->o_arg.seqid == NULL) // seqid分配失敗.
goto err_free;
nfs_sb_active(dentry->d_sb);
p->dentry = dget(dentry); // 這是要打開文件的目錄項結構
p->dir = parent; // 設置父目錄的索引節點
p->owner = sp; // nfs4_state_owner結構
atomic_inc(&sp->so_count); // 增加這個結構的引用計數
p->o_arg.fh = NFS_FH(dir); // 父目錄的文件句柄
p->o_arg.open_flags = flags; // 打開目標文件時設置的標誌信息,如O_CREAT
p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE); // 文件的訪問模式
p->o_arg.clientid = server->nfs_client->cl_clientid; // 這表示一個NFS客戶端
// nfs_seqid_counter的創建時間
p->o_arg.id.create_time = ktime_to_ns(sp->so_seqid.create_time);
// 這是客戶端爲nfs4_state_owner結構設置的編號
p->o_arg.id.uniquifier = sp->so_seqid.owner_id;
p->o_arg.name = &dentry->d_name; // 目標文件的名稱
p->o_arg.server = server; // nfs_server結構
p->o_arg.bitmask = server->attr_bitmask; // NFS服務器端文件系統支持的屬性信息
p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0]; // 需要查找的基本屬性信息
p->o_arg.claim = NFS4_OPEN_CLAIM_NULL; // 正常打開文件.
// 這是爲OPEN4_CREATE設置的信息
if (attrs != NULL && attrs->ia_valid != 0) {
__be32 verf[2];
p->o_arg.u.attrs = &p->attrs;
memcpy(&p->attrs, attrs, sizeof(p->attrs));
verf[0] = jiffies; // 當前時間
verf[1] = current->pid; // 進程的pid
memcpy(p->o_arg.u.verifier.data, verf,
sizeof(p->o_arg.u.verifier.data));
}
p->c_arg.fh = &p->o_res.fh;
p->c_arg.stateid = &p->o_res.stateid;
p->c_arg.seqid = p->o_arg.seqid;
nfs4_init_opendata_res(p);
kref_init(&p->kref);
return p;
err_free:
kfree(p);
err:
dput(parent);
return NULL;
}
函數encode_open()根據nfs4_opendata_alloc()設置的信息封裝OPEN請求報文。
static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr)
{
encode_op_hdr(xdr, OP_OPEN, decode_open_maxsz, hdr); // OPEN請求編號
encode_openhdr(xdr, arg);
encode_opentype(xdr, arg); // openhow,取值爲OPEN4_CREATE或者OPEN4_NOCREATE
switch (arg->claim) { // claim 通常情況下取值是NFS4_OPEN_CLAIM_NULL
case NFS4_OPEN_CLAIM_NULL:
encode_claim_null(xdr, arg->name);
break;
case NFS4_OPEN_CLAIM_PREVIOUS:
encode_claim_previous(xdr, arg->u.delegation_type);
break;
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
break;
default:
BUG();
}
}
static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_openargs *arg)
{
__be32 *p;
/*
* opcode 4, seqid 4, share_access 4, share_deny 4, clientid 8, ownerlen 4,
* owner 4 = 32
*/
// 這是seqid
encode_nfs4_seqid(xdr, arg->seqid); // arg->seqid->sequence->counter seqid
// share_accessed 文件訪問權限 只讀 0x00000001 只寫 0x00000002 讀寫 0x00000003
// share_deny = 0 always
encode_share_access(xdr, arg->fmode); // 編碼share_access和share_deny
p = reserve_space(xdr, 36);
p = xdr_encode_hyper(p, arg->clientid); // 編碼clientid 8字節
*p++ = cpu_to_be32(24); // owner 長度固定爲24字節
p = xdr_encode_opaque_fixed(p, "open id:", 8); // 8字節
*p++ = cpu_to_be32(arg->server->s_dev); // 4字節 // 設備號
*p++ = cpu_to_be32(arg->id.uniquifier); // nfs4_state_owner結構的編號 4字節
xdr_encode_hyper(p, arg->id.create_time); // nfs4_state_owner結構的創建時間 8字節
}
對照RFC3530的規定,請求報文中的owner字段由clientid和這次請求操作的名稱兩部分構成,名稱字段長度固定爲24字節,包含了字符串"open id:"(8字節)、NFS文件系統所在設備的設備號(4字節)、用戶信息數據結構的編號(4字節)、用戶信息結構的創建時間(8字節)。