📄 proc.c
字号:
smb_close_socket(server); if (pid == 0) { printk(KERN_ERR "smb_retry: no connection process\n"); server->state = CONN_RETRIED; goto out; } /* * Clear the pid to enable the ioctl. */ server->conn_pid = 0; /* * Note: use the "priv" flag, as a user process may need to reconnect. */ error = kill_proc(pid, SIGUSR1, 1); if (error) { printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error); goto out_restore; } VERBOSE("signalled pid %d, waiting for new connection\n", pid); /* * Wait for the new connection. */#ifdef SMB_RETRY_INTR interruptible_sleep_on_timeout(&server->wait, 5*HZ); if (signal_pending(current)) printk(KERN_INFO "smb_retry: caught signal\n");#else /* * We don't want to be interrupted. For example, what if 'current' * already has recieved a signal? sleep_on would terminate immediately * and smbmount would not be able to re-establish connection. * * smbmount should be able to reconnect later, but it can't because * it will get an -EIO on attempts to open the mountpoint! */ sleep_on_timeout(&server->wait, 5*HZ);#endif /* * Check for a valid connection. */ if (server->state == CONN_VALID) { /* This should be changed to VERBOSE, except many smbfs problems is with the userspace daemon not reconnecting. */ PARANOIA("sucessful, new pid=%d, generation=%d\n", server->conn_pid, server->generation); result = 1; } /* * Restore the original pid if we didn't get a new one. */out_restore: if (!server->conn_pid) server->conn_pid = pid;out: return result;}/* smb_request_ok: We expect the server to be locked. Then we do the request and check the answer completely. When smb_request_ok returns 0, you can be quite sure that everything went well. When the answer is <=0, the returned number is a valid unix errno. */static intsmb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc){ int result = -EIO; s->rcls = 0; s->err = 0; /* Make sure we have a connection */ if (s->state != CONN_VALID) { if (!smb_retry(s)) goto out; } if (smb_request(s) < 0) { DEBUG1("smb_request failed\n"); goto out; } if (smb_valid_packet(s->packet) != 0) { PARANOIA("invalid packet!\n"); goto out; } /* * Check for server errors. The current smb_errno() routine * is squashing some error codes, but I don't think this is * correct: after a server error the packet won't be valid. */ if (s->rcls != 0) { result = -smb_errno(s); if (!result) printk(KERN_DEBUG "smb_request_ok: rcls=%d, err=%d mapped to 0\n", s->rcls, s->err); /* * Exit now even if the error was squashed ... * packet verify will fail anyway. */ goto out; } result = smb_verify(s->packet, command, wct, bcc);out: return result;}/* * This implements the NEWCONN ioctl. It installs the server pid, * sets server->state to CONN_VALID, and wakes up the waiting process. * * Note that this must be called with the server locked, except for * the first call made after mounting the volume. The server pid * will be set to zero to indicate that smbfs is awaiting a connection. */intsmb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt){ struct file *filp; int error; VERBOSE("fd=%d, pid=%d\n", opt->fd, current->pid); /* * Make sure we don't already have a pid ... */ error = -EINVAL; if (server->conn_pid) goto out; error = -EACCES; if (current->uid != server->mnt->mounted_uid && !capable(CAP_SYS_ADMIN)) goto out; error = -EBADF; filp = fget(opt->fd); if (!filp) goto out; if (!smb_valid_socket(filp->f_dentry->d_inode)) goto out_putf; server->sock_file = filp; server->conn_pid = current->pid; smb_catch_keepalive(server); server->opt = *opt; server->generation += 1; server->state = CONN_VALID; error = 0; /* check if we have an old smbmount that uses seconds for the serverzone */ if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60) server->opt.serverzone /= 60; /* now that we have an established connection we can detect the server type and enable bug workarounds */ if (server->opt.protocol == SMB_PROTOCOL_NT1 && (server->opt.max_xmit < 0x1000) && !(server->opt.capabilities & SMB_CAP_NT_SMBS)) { server->mnt->flags |= SMB_MOUNT_WIN95;#ifdef SMBFS_DEBUG_VERBOSE printk(KERN_NOTICE "smb_newconn: detected WIN95 server\n");#endif } VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n", server->opt.protocol, server->opt.max_xmit, server->conn_pid, server->opt.capabilities);out:#ifdef SMB_RETRY_INTR wake_up_interruptible(&server->wait);#else wake_up(&server->wait);#endif return error;out_putf: fput(filp); goto out;}/* smb_setup_header: We completely set up the packet. You only have to insert the command-specific fields */__u8 *smb_setup_header(struct smb_sb_info * server, __u8 command, __u16 wct, __u16 bcc){ __u32 xmit_len = SMB_HEADER_LEN + wct * sizeof(__u16) + bcc + 2; __u8 *p = server->packet; __u8 *buf = server->packet; if (xmit_len > server->packet_size) printk(KERN_DEBUG "smb_setup_header: " "Aieee, xmit len > packet! len=%d, size=%d\n", xmit_len, server->packet_size); p = smb_encode_smb_length(p, xmit_len - 4); *p++ = 0xff; *p++ = 'S'; *p++ = 'M'; *p++ = 'B'; *p++ = command; memset(p, '\0', 19); p += 19; p += 8; WSET(buf, smb_tid, server->opt.tid); WSET(buf, smb_pid, 1); WSET(buf, smb_uid, server->opt.server_uid); WSET(buf, smb_mid, 1); if (server->opt.protocol > SMB_PROTOCOL_CORE) { *(buf+smb_flg) = 0x8; WSET(buf, smb_flg2, 0x3); } *p++ = wct; /* wct */ p += 2 * wct; WSET(p, 0, bcc); return p + 2;}static voidsmb_setup_bcc(struct smb_sb_info *server, __u8 * p){ __u8 *packet = server->packet; __u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet); __u16 bcc = p - (pbcc + 2); WSET(pbcc, 0, bcc); smb_encode_smb_length(packet, SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);}/* * We're called with the server locked, and we leave it that way. */static intsmb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish){ struct inode *ino = dentry->d_inode; int mode, read_write = 0x42, read_only = 0x40; int res; char *p; /* * 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. */ if (!(wish & (O_WRONLY | O_RDWR))) mode = read_only;#endif retry: p = smb_setup_header(server, SMBopen, 2, 0); WSET(server->packet, smb_vwv0, mode); WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); *p++ = 4; res = smb_encode_path(server, p, dentry, NULL); if (res < 0) goto out; p += res; smb_setup_bcc(server, p); res = smb_request_ok(server, SMBopen, 7, 0); if (res != 0) { if (smb_retry(server)) goto retry; 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; goto retry; } goto out; } /* We should now have data in vwv[0..6]. */ ino->u.smbfs_i.fileid = WVAL(server->packet, smb_vwv0); ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1); /* smb_vwv2 has mtime */ /* smb_vwv4 has size */ ino->u.smbfs_i.access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK); if (!(wish & (O_WRONLY | O_RDWR))) ino->u.smbfs_i.access = SMB_O_RDONLY; ino->u.smbfs_i.open = server->generation;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; 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 = SMB_SERVER(inode); smb_lock_server(server); result = 0; if (!smb_is_open(inode)) result = smb_proc_open(server, dentry, wish); smb_unlock_server(server); if (result) { PARANOIA("%s/%s open failed, result=%d\n", DENTRY_PATH(dentry), 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; if (inode->u.smbfs_i.access != wish && inode->u.smbfs_i.access != SMB_O_RDWR) { PARANOIA("%s/%s access denied, access=%x, wish=%x\n", DENTRY_PATH(dentry), inode->u.smbfs_i.access, wish); result = -EACCES; }out: return result;}/* We're called with the server locked */static int smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime){ smb_setup_header(server, SMBclose, 3, 0); WSET(server->packet, smb_vwv0, fileid); DSET(server->packet, smb_vwv1, utc2local(server, mtime)); return smb_request_ok(server, SMBclose, 0, 0);}/* * Called with the server locked. * * 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){ 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. */ ino->u.smbfs_i.open = 0; /* * Kludge alert: SMB timestamps are accurate only to * two seconds ... round the times to avoid needless * cache invalidations! */ if (ino->i_mtime & 1) ino->i_mtime--; if (ino->i_atime & 1) ino->i_atime--; /* * If the file is open with write permissions, * update the time stamps to sync mtime and atime. */ if ((server->opt.protocol >= SMB_PROTOCOL_LANMAN2) && !(ino->u.smbfs_i.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, ino->u.smbfs_i.fileid, ino->i_mtime); ino->u.smbfs_i.cache_valid &= ~SMB_F_LOCALWRITE; /* * 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) ino->u.smbfs_i.oldmtime = 0; ino->u.smbfs_i.closed = jiffies; } return result;}intsmb_close(struct inode *ino){ int result = 0; if (smb_is_open(ino)) { struct smb_sb_info *server = SMB_SERVER(ino); smb_lock_server(server); result = smb_proc_close_inode(server, ino); smb_unlock_server(server); } 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; smb_lock_server(server); result = smb_proc_close(server, fileid, CURRENT_TIME); smb_unlock_server(server); 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. */intsmb_proc_read(struct inode *inode, off_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; smb_lock_server(server); smb_setup_header(server, SMBread, 5, 0); buf = server->packet; WSET(buf, smb_vwv0, inode->u.smbfs_i.fileid); WSET(buf, smb_vwv1, count); DSET(buf, smb_vwv2, offset); WSET(buf, smb_vwv4, 0); result = smb_request_ok(server, SMBread, 5, -1); if (result < 0) goto out; returned_count = WVAL(server->packet, smb_vwv0); buf = SMB_BUF(server->packet); data_len = WVAL(buf, 1); /* we can NOT simply trust the data_len given by the server ... */ if (data_len > server->packet_size - (buf+3 - server->packet)) { printk(KERN_ERR "smb_proc_read: invalid data length!! " "%d > %d - (%p - %p)\n", data_len, server->packet_size, buf+3, server->packet); result = -EIO; goto out; } memcpy(data, buf+3, data_len); 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: VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n", inode->ino, inode->u.smbfs_i.fileid, count, result); smb_unlock_server(server); return result;}intsmb_proc_write(struct inode *inode, off_t offset, int count, const char *data){ struct smb_sb_info *server = server_from_inode(inode); int result; __u8 *p; VERBOSE("ino=%ld, fileid=%d, count=%d@%ld, packet_size=%d\n", inode->ino, inode->u.smbfs_i.fileid, count, offset, server->packet_size); smb_lock_server(server); p = smb_setup_header(server, SMBwrite, 5, count + 3); WSET(server->packet, smb_vwv0, inode->u.smbfs_i.fileid); WSET(server->packet, smb_vwv1, count); DSET(server->packet, smb_vwv2, offset); WSET(server->packet, smb_vwv4, 0); *p++ = 1; WSET(p, 0, count); memcpy(p+2, data, count); result = smb_request_ok(server, SMBwrite, 1, 0); if (result >= 0) result = WVAL(server->packet, smb_vwv0); smb_unlock_server(server); 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; smb_lock_server(server); retry: p = smb_setup_header(server, SMBcreate, 3, 0); WSET(server->packet, smb_vwv0, attr); DSET(server->packet, smb_vwv1, utc2local(server, ctime)); *p++ = 4; result = smb_encode_path(server, p, dentry, NULL); if (result < 0) goto out; p += result; smb_setup_bcc(server, p); result = smb_request_ok(server, SMBcreate, 1, 0); if (result < 0) { if (smb_retry(server)) goto retry; goto out; } *fileid = WVAL(server->packet, smb_vwv0); result = 0;out: smb_unlock_server(server); 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; smb_lock_server(server); retry: p = smb_setup_header(server, SMBmv, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR); *p++ = 4; result = smb_encode_path(server, p, old_dentry, NULL); if (result < 0) goto out; p += result; *p++ = 4; result = smb_encode_path(server, p, new_dentry, NULL); if (result < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -