📄 proc.c
字号:
result = -ENOMEM; if (! (req = smb_alloc_request(server, 0))) goto out; smb_setup_header(req, SMBlseek, 4, 0); WSET(req->rq_header, smb_vwv0, fileid); WSET(req->rq_header, smb_vwv1, mode); DSET(req->rq_header, smb_vwv2, offset); req->rq_flags |= SMB_REQ_NORETRY; result = smb_request_ok(req, SMBlseek, 2, 0); if (result < 0) { result = 0; goto out_free; } result = DVAL(req->rq_header, smb_vwv0);out_free: smb_rput(req);out: return result;}static intsmb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish){ struct inode *ino = dentry->d_inode; struct smb_inode_info *ei = SMB_I(ino); int mode, read_write = 0x42, read_only = 0x40; int res; char *p; struct smb_request *req; /* * Attempt to open r/w, unless there are no write privileges. */ mode = read_write; if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode = read_only;#if 0 /* FIXME: why is this code not in? below we fix it so that a caller wanting RO doesn't get RW. smb_revalidate_inode does some optimization based on access mode. tail -f needs it to be correct. We must open rw since we don't do the open if called a second time with different 'wish'. Is that not supported by smb servers? */ if (!(wish & (O_WRONLY | O_RDWR))) mode = read_only;#endif res = -ENOMEM; if (! (req = smb_alloc_request(server, PAGE_SIZE))) goto out; retry: p = smb_setup_header(req, SMBopen, 2, 0); WSET(req->rq_header, smb_vwv0, mode); WSET(req->rq_header, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); res = smb_simple_encode_path(req, &p, dentry, NULL); if (res < 0) goto out_free; smb_setup_bcc(req, p); res = smb_request_ok(req, SMBopen, 7, 0); if (res != 0) { if (mode == read_write && (res == -EACCES || res == -ETXTBSY || res == -EROFS)) { VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n", DENTRY_PATH(dentry), res); mode = read_only; req->rq_flags = 0; goto retry; } goto out_free; } /* We should now have data in vwv[0..6]. */ ei->fileid = WVAL(req->rq_header, smb_vwv0); ei->attr = WVAL(req->rq_header, smb_vwv1); /* smb_vwv2 has mtime */ /* smb_vwv4 has size */ ei->access = (WVAL(req->rq_header, smb_vwv6) & SMB_ACCMASK); ei->open = server->generation;out_free: smb_rput(req);out: return res;}/* * Make sure the file is open, and check that the access * is compatible with the desired access. */intsmb_open(struct dentry *dentry, int wish){ struct inode *inode = dentry->d_inode; int result; __u16 access; result = -ENOENT; if (!inode) { printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n", DENTRY_PATH(dentry)); goto out; } if (!smb_is_open(inode)) { struct smb_sb_info *server = server_from_inode(inode); result = 0; if (!smb_is_open(inode)) result = smb_proc_open(server, dentry, wish); if (result) goto out; /* * A successful open means the path is still valid ... */ smb_renew_times(dentry); } /* * Check whether the access is compatible with the desired mode. */ result = 0; access = SMB_I(inode)->access; if (access != wish && access != SMB_O_RDWR) { PARANOIA("%s/%s access denied, access=%x, wish=%x\n", DENTRY_PATH(dentry), access, wish); result = -EACCES; }out: return result;}static int smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime){ struct smb_request *req; int result = -ENOMEM; if (! (req = smb_alloc_request(server, 0))) goto out; smb_setup_header(req, SMBclose, 3, 0); WSET(req->rq_header, smb_vwv0, fileid); DSET(req->rq_header, smb_vwv1, utc2local(server, mtime)); req->rq_flags |= SMB_REQ_NORETRY; result = smb_request_ok(req, SMBclose, 0, 0); smb_rput(req);out: return result;}/* * Win NT 4.0 has an apparent bug in that it fails to update the * modify time when writing to a file. As a workaround, we update * both modify and access time locally, and post the times to the * server when closing the file. */static int smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino){ struct smb_inode_info *ei = SMB_I(ino); int result = 0; if (smb_is_open(ino)) { /* * We clear the open flag in advance, in case another * process observes the value while we block below. */ ei->open = 0; /* * Kludge alert: SMB timestamps are accurate only to * two seconds ... round the times to avoid needless * cache invalidations! */ if (ino->i_mtime.tv_sec & 1) { ino->i_mtime.tv_sec--; ino->i_mtime.tv_nsec = 0; } if (ino->i_atime.tv_sec & 1) { ino->i_atime.tv_sec--; ino->i_atime.tv_nsec = 0; } /* * If the file is open with write permissions, * update the time stamps to sync mtime and atime. */ if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 && (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && !(ei->access == SMB_O_RDONLY)) { struct smb_fattr fattr; smb_get_inode_attr(ino, &fattr); smb_proc_setattr_ext(server, ino, &fattr); } result = smb_proc_close(server, ei->fileid, ino->i_mtime.tv_sec); /* * Force a revalidation after closing ... some servers * don't post the size until the file has been closed. */ if (server->opt.protocol < SMB_PROTOCOL_NT1) ei->oldmtime = 0; ei->closed = jiffies; } return result;}intsmb_close(struct inode *ino){ int result = 0; if (smb_is_open(ino)) { struct smb_sb_info *server = server_from_inode(ino); result = smb_proc_close_inode(server, ino); } return result;}/* * This is used to close a file following a failed instantiate. * Since we don't have an inode, we can't use any of the above. */intsmb_close_fileid(struct dentry *dentry, __u16 fileid){ struct smb_sb_info *server = server_from_dentry(dentry); int result; result = smb_proc_close(server, fileid, get_seconds()); return result;}/* In smb_proc_read and smb_proc_write we do not retry, because the file-id would not be valid after a reconnection. */static voidsmb_proc_read_data(struct smb_request *req){ req->rq_iov[0].iov_base = req->rq_buffer; req->rq_iov[0].iov_len = 3; req->rq_iov[1].iov_base = req->rq_page; req->rq_iov[1].iov_len = req->rq_rsize; req->rq_iovlen = 2; req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;}static intsmb_proc_read(struct inode *inode, loff_t offset, int count, char *data){ struct smb_sb_info *server = server_from_inode(inode); __u16 returned_count, data_len; unsigned char *buf; int result; struct smb_request *req; u8 rbuf[4]; result = -ENOMEM; if (! (req = smb_alloc_request(server, 0))) goto out; smb_setup_header(req, SMBread, 5, 0); buf = req->rq_header; WSET(buf, smb_vwv0, SMB_I(inode)->fileid); WSET(buf, smb_vwv1, count); DSET(buf, smb_vwv2, offset); WSET(buf, smb_vwv4, 0); req->rq_page = data; req->rq_rsize = count; req->rq_callback = smb_proc_read_data; req->rq_buffer = rbuf; req->rq_flags |= SMB_REQ_NORETRY | SMB_REQ_STATIC; result = smb_request_ok(req, SMBread, 5, -1); if (result < 0) goto out_free; returned_count = WVAL(req->rq_header, smb_vwv0); data_len = WVAL(rbuf, 1); if (returned_count != data_len) { printk(KERN_NOTICE "smb_proc_read: returned != data_len\n"); printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n", returned_count, data_len); } result = data_len;out_free: smb_rput(req);out: VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", inode->i_ino, SMB_I(inode)->fileid, count, result); return result;}static intsmb_proc_write(struct inode *inode, loff_t offset, int count, const char *data){ struct smb_sb_info *server = server_from_inode(inode); int result; u16 fileid = SMB_I(inode)->fileid; u8 buf[4]; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, 0))) goto out; VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", inode->i_ino, fileid, count, offset); smb_setup_header(req, SMBwrite, 5, count + 3); WSET(req->rq_header, smb_vwv0, fileid); WSET(req->rq_header, smb_vwv1, count); DSET(req->rq_header, smb_vwv2, offset); WSET(req->rq_header, smb_vwv4, 0); buf[0] = 1; WSET(buf, 1, count); /* yes, again ... */ req->rq_iov[1].iov_base = buf; req->rq_iov[1].iov_len = 3; req->rq_iov[2].iov_base = (char *) data; req->rq_iov[2].iov_len = count; req->rq_iovlen = 3; req->rq_flags |= SMB_REQ_NORETRY; result = smb_request_ok(req, SMBwrite, 1, 0); if (result >= 0) result = WVAL(req->rq_header, smb_vwv0); smb_rput(req);out: return result;}/* * In smb_proc_readX and smb_proc_writeX we do not retry, because the * file-id would not be valid after a reconnection. */#define SMB_READX_MAX_PAD 64static voidsmb_proc_readX_data(struct smb_request *req){ /* header length, excluding the netbios length (-4) */ int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2; int data_off = WVAL(req->rq_header, smb_vwv6); /* * Some genius made the padding to the data bytes arbitrary. * So we must first calculate the amount of padding used by the server. */ data_off -= hdrlen; if (data_off > SMB_READX_MAX_PAD || data_off < 0) { PARANOIA("offset is larger than SMB_READX_MAX_PAD or negative!\n"); PARANOIA("%d > %d || %d < 0\n", data_off, SMB_READX_MAX_PAD, data_off); req->rq_rlen = req->rq_bufsize + 1; return; } req->rq_iov[0].iov_base = req->rq_buffer; req->rq_iov[0].iov_len = data_off; req->rq_iov[1].iov_base = req->rq_page; req->rq_iov[1].iov_len = req->rq_rsize; req->rq_iovlen = 2; req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;}static intsmb_proc_readX(struct inode *inode, loff_t offset, int count, char *data){ struct smb_sb_info *server = server_from_inode(inode); unsigned char *buf; int result; struct smb_request *req; static char pad[SMB_READX_MAX_PAD]; result = -ENOMEM; if (! (req = smb_alloc_request(server, 0))) goto out; smb_setup_header(req, SMBreadX, 12, 0); buf = req->rq_header; WSET(buf, smb_vwv0, 0x00ff); WSET(buf, smb_vwv1, 0); WSET(buf, smb_vwv2, SMB_I(inode)->fileid); DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */ WSET(buf, smb_vwv5, count); WSET(buf, smb_vwv6, 0); DSET(buf, smb_vwv7, 0); WSET(buf, smb_vwv9, 0); DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */ WSET(buf, smb_vwv11, 0); req->rq_page = data; req->rq_rsize = count; req->rq_callback = smb_proc_readX_data; req->rq_buffer = pad; req->rq_bufsize = SMB_READX_MAX_PAD; req->rq_flags |= SMB_REQ_STATIC | SMB_REQ_NORETRY; result = smb_request_ok(req, SMBreadX, 12, -1); if (result < 0) goto out_free; result = WVAL(req->rq_header, smb_vwv5);out_free: smb_rput(req);out: VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", inode->i_ino, SMB_I(inode)->fileid, count, result); return result;}static intsmb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data){ struct smb_sb_info *server = server_from_inode(inode); int result; u8 *p; static u8 pad[4]; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, 0))) goto out; VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld\n", inode->i_ino, SMB_I(inode)->fileid, count, offset); p = smb_setup_header(req, SMBwriteX, 14, count + 1); WSET(req->rq_header, smb_vwv0, 0x00ff); WSET(req->rq_header, smb_vwv1, 0); WSET(req->rq_header, smb_vwv2, SMB_I(inode)->fileid); DSET(req->rq_header, smb_vwv3, (u32)offset); /* low 32 bits */ DSET(req->rq_header, smb_vwv5, 0); WSET(req->rq_header, smb_vwv7, 0); /* write mode */ WSET(req->rq_header, smb_vwv8, 0); WSET(req->rq_header, smb_vwv9, 0); WSET(req->rq_header, smb_vwv10, count); /* data length */ WSET(req->rq_header, smb_vwv11, smb_vwv12 + 2 + 1); DSET(req->rq_header, smb_vwv12, (u32)(offset >> 32)); req->rq_iov[1].iov_base = pad; req->rq_iov[1].iov_len = 1; req->rq_iov[2].iov_base = (char *) data; req->rq_iov[2].iov_len = count; req->rq_iovlen = 3; req->rq_flags |= SMB_REQ_NORETRY; result = smb_request_ok(req, SMBwriteX, 6, 0); if (result >= 0) result = WVAL(req->rq_header, smb_vwv2); smb_rput(req);out: return result;}intsmb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid){ struct smb_sb_info *server = server_from_dentry(dentry); char *p; int result; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, PAGE_SIZE))) goto out; p = smb_setup_header(req, SMBcreate, 3, 0); WSET(req->rq_header, smb_vwv0, attr); DSET(req->rq_header, smb_vwv1, utc2local(server, ctime)); result = smb_simple_encode_path(req, &p, dentry, NULL); if (result < 0) goto out_free; smb_setup_bcc(req, p); result = smb_request_ok(req, SMBcreate, 1, 0); if (result < 0) goto out_free; *fileid = WVAL(req->rq_header, smb_vwv0); result = 0;out_free: smb_rput(req);out: return result;}intsmb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry){ struct smb_sb_info *server = server_from_dentry(old_dentry); char *p; int result; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, PAGE_SIZE))) goto out; p = smb_setup_header(req, SMBmv, 1, 0); WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); result = smb_simple_encode_path(req, &p, old_dentry, NULL); if (result < 0) goto out_free; result = smb_simple_encode_path(req, &p, new_dentry, NULL); if (result < 0) goto out_free; smb_setup_bcc(req, p); if ((result = smb_request_ok(req, SMBmv, 0, 0)) < 0) goto out_free; result = 0;out_free: smb_rput(req);out: return result;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -