📄 proc.c
字号:
VERBOSE("packet=%d, xmit=%d, size=%d\n", server->packet_size, server->opt.max_xmit, size); return size;}/* * Convert SMB error codes to -E... errno values. */intsmb_errno(struct smb_sb_info *server){ int errcls = server->rcls; int error = server->err; char *class = "Unknown"; VERBOSE("errcls %d code %d from command 0x%x\n", errcls, error, SMB_CMD(server->packet)); if (errcls == ERRDOS) { switch (error) { case ERRbadfunc: return -EINVAL; case ERRbadfile: case ERRbadpath: return -ENOENT; case ERRnofids: return -EMFILE; case ERRnoaccess: return -EACCES; case ERRbadfid: return -EBADF; case ERRbadmcb: return -EREMOTEIO; case ERRnomem: return -ENOMEM; case ERRbadmem: return -EFAULT; case ERRbadenv: case ERRbadformat: return -EREMOTEIO; case ERRbadaccess: return -EACCES; case ERRbaddata: return -E2BIG; case ERRbaddrive: return -ENXIO; case ERRremcd: return -EREMOTEIO; case ERRdiffdevice: return -EXDEV; case ERRnofiles: return -ENOENT; case ERRbadshare: return -ETXTBSY; case ERRlock: return -EDEADLK; case ERRfilexists: return -EEXIST; case ERROR_INVALID_PARAMETER: return -EINVAL; case ERROR_DISK_FULL: return -ENOSPC; case ERROR_INVALID_NAME: return -ENOENT; case ERROR_DIR_NOT_EMPTY: return -ENOTEMPTY; case ERROR_NOT_LOCKED: return -ENOLCK; case ERROR_ALREADY_EXISTS: return -EEXIST; default: class = "ERRDOS"; goto err_unknown; } } else if (errcls == ERRSRV) { switch (error) { /* N.B. This is wrong ... EIO ? */ case ERRerror: return -ENFILE; case ERRbadpw: return -EINVAL; case ERRbadtype: return -EIO; case ERRaccess: return -EACCES; /* * This is a fatal error, as it means the "tree ID" * for this connection is no longer valid. We map * to a special error code and get a new connection. */ case ERRinvnid: return -EBADSLT; default: class = "ERRSRV"; goto err_unknown; } } else if (errcls == ERRHRD) { switch (error) { case ERRnowrite: return -EROFS; case ERRbadunit: return -ENODEV; case ERRnotready: return -EUCLEAN; case ERRbadcmd: case ERRdata: return -EIO; case ERRbadreq: return -ERANGE; case ERRbadshare: return -ETXTBSY; case ERRlock: return -EDEADLK; case ERRdiskfull: return -ENOSPC; default: class = "ERRHRD"; goto err_unknown; } } else if (errcls == ERRCMD) { class = "ERRCMD"; } else if (errcls == SUCCESS) { return 0; /* This is the only valid 0 return */ }err_unknown: printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n", class, error, SMB_CMD(server->packet)); return -EIO;}/* * smb_retry: This function should be called when smb_request_ok has * indicated an error. If the error was indicated because the * connection was killed, we try to reconnect. If smb_retry returns 0, * the error was indicated for another reason, so a retry would not be * of any use. * N.B. The server must be locked for this call. */static intsmb_retry(struct smb_sb_info *server){ pid_t pid = server->conn_pid; int error, result = 0; if (server->state == CONN_VALID || server->state == CONN_RETRYING) goto out; smb_close_socket(server); if (pid == 0) { printk(KERN_ERR "smb_retry: no connection process\n"); server->state = CONN_RETRIED; goto out; } /* * Change state so that only one retry per server will be started. */ server->state = CONN_RETRYING; /* * Note: use the "priv" flag, as a user process may need to reconnect. */ error = kill_proc(pid, SIGUSR1, 1); if (error) { /* FIXME: this is fatal */ printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error); goto out; } VERBOSE("signalled pid %d, waiting for new connection\n", pid); /* * Wait for the new connection. */#ifdef SMB_RETRY_INTR smb_unlock_server(server); interruptible_sleep_on_timeout(&server->wait, 30*HZ); smb_lock_server(server); 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 received 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! * * FIXME: go back to the interruptable version now that smbmount * can avoid -EIO on the mountpoint when reconnecting? */ smb_unlock_server(server); sleep_on_timeout(&server->wait, 30*HZ); smb_lock_server(server);#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("successful, new pid=%d, generation=%d\n", server->conn_pid, server->generation); result = 1; } else if (server->state == CONN_RETRYING) { /* allow further attempts later */ server->state = CONN_RETRIED; }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. */ 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. */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); smb_lock_server(server); /* * Make sure we don't already have a valid connection ... */ error = -EINVAL; if (server->state == CONN_VALID) 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; VERBOSE("smb_newconn: detected WIN95 server\n"); } 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); /* Make sure we can fit a message of the negotiated size in our packet buffer. */ if (server->opt.max_xmit > server->packet_size) { int len = smb_round_length(server->opt.max_xmit); char *buf = smb_vmalloc(len); if (buf) { if (server->packet) smb_vfree(server->packet); server->packet = buf; server->packet_size = len; } else { /* else continue with the too small buffer? */ PARANOIA("Failed to allocate new packet buffer: " "max_xmit=%d, packet_size=%d\n", server->opt.max_xmit, server->packet_size); server->opt.max_xmit = server->packet_size; } }out: smb_unlock_server(server); smb_wakeup(server); return error;out_putf: fput(filp); goto out;}intsmb_wakeup(struct smb_sb_info *server){#ifdef SMB_RETRY_INTR wake_up_interruptible(&server->wait);#else wake_up(&server->wait);#endif return 0;}/* 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);}/* * Called with the server locked */static intsmb_proc_seek(struct smb_sb_info *server, __u16 fileid, __u16 mode, off_t offset){ int result; smb_setup_header(server, SMBlseek, 4, 0); WSET(server->packet, smb_vwv0, fileid); WSET(server->packet, smb_vwv1, mode); DSET(server->packet, smb_vwv2, offset); result = smb_request_ok(server, SMBlseek, 2, 0); if (result < 0) { result = 0; goto out; } result = DVAL(server->packet, smb_vwv0);out: return result;}/* * 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. 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 retry: p = smb_setup_header(server, SMBopen, 2, 0); WSET(server->packet, smb_vwv0, mode); WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); res = smb_simple_encode_path(server, &p, dentry, NULL); if (res < 0) goto out; 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); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -