📄 proc.c
字号:
/* * proc.c * * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke * Copyright (C) 1997 by Volker Lendecke * * Please add a note about your changes to smbfs in the ChangeLog file. */#include <linux/types.h>#include <linux/errno.h>#include <linux/malloc.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/stat.h>#include <linux/fcntl.h>#include <linux/dcache.h>#include <linux/dirent.h>#include <linux/nls.h>#include <linux/smb_fs.h>#include <linux/smbno.h>#include <linux/smb_mount.h>#include <asm/string.h>#include "smb_debug.h"/* Features. Undefine if they cause problems, this should perhaps be a config option. */#define SMBFS_POSIX_UNLINK 1/* Allow smb_retry to be interrupted. Not sure of the benefit ... *//* #define SMB_RETRY_INTR */#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)#define SMB_CMD(packet) (*(packet+8))#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1))#define SMB_BCC(packet) smb_bcc(packet)#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)#define SMB_DIRINFO_SIZE 43#define SMB_STATUS_SIZE 21static intsmb_proc_setattr_ext(struct smb_sb_info *, struct inode *, struct smb_fattr *);static intsmb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, __u16 attr);static intsmb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr);static inline voidsmb_lock_server(struct smb_sb_info *server){ down(&(server->sem));}static inline voidsmb_unlock_server(struct smb_sb_info *server){ up(&(server->sem));}static voidstr_upper(char *name, int len){ while (len--) { if (*name >= 'a' && *name <= 'z') *name -= ('a' - 'A'); name++; }}static voidstr_lower(char *name, int len){ while (len--) { if (*name >= 'A' && *name <= 'Z') *name += ('a' - 'A'); name++; }}/* reverse a string inline. This is used by the dircache walking routines */static void reverse_string(char *buf, int len){ char c; char *end = buf+len-1; while(buf < end) { c = *buf; *(buf++) = *end; *(end--) = c; }}/* no conversion, just a wrapper for memcpy. */static int convert_memcpy(char *output, int olen, const char *input, int ilen, struct nls_table *nls_from, struct nls_table *nls_to){ memcpy(output, input, ilen); return ilen;}/* convert from one "codepage" to another (possibly being utf8). */static int convert_cp(char *output, int olen, const char *input, int ilen, struct nls_table *nls_from, struct nls_table *nls_to){ int len = 0; int n; wchar_t ch; if (!nls_from || !nls_to) { PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to); return convert_memcpy(output, olen, input, ilen, NULL, NULL); } while (ilen > 0) { /* convert by changing to unicode and back to the new cp */ n = nls_from->char2uni((unsigned char *)input, ilen, &ch); if (n < 0) goto out; input += n; ilen -= n; n = nls_to->uni2char(ch, output, olen); if (n < 0) goto out; output += n; olen -= n; len += n; }out: return len;}static int setcodepage(struct smb_sb_info *server, struct nls_table **p, char *name){ struct nls_table *nls; if (!name || !*name) { nls = NULL; } else if ( (nls = load_nls(name)) == NULL) { printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name); return -EINVAL; } /* if already set, unload the previous one. */ if (*p) unload_nls(*p); *p = nls; return 0;}/* Handles all changes to codepage settings. */int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp){ int n; smb_lock_server(server); n = setcodepage(server, &server->local_nls, cp->local_name); if (n != 0) goto out; n = setcodepage(server, &server->remote_nls, cp->remote_name); if (n != 0) setcodepage(server, &server->local_nls, NULL);out: if (server->local_nls != NULL && server->remote_nls != NULL) server->convert = convert_cp; else server->convert = convert_memcpy; smb_unlock_server(server); return n;}/*****************************************************************************//* *//* Encoding/Decoding section *//* *//*****************************************************************************/__u8 *smb_encode_smb_length(__u8 * p, __u32 len){ *p = 0; *(p+1) = 0; *(p+2) = (len & 0xFF00) >> 8; *(p+3) = (len & 0xFF); if (len > 0xFFFF) { *(p+1) = 1; } return p + 4;}/* * smb_build_path: build the path to entry and name storing it in buf. * The path returned will have the trailing '\0'. */static int smb_build_path(struct smb_sb_info *server, char * buf, struct dentry * entry, struct qstr * name){ char *path = buf; int len; if (entry == NULL) goto test_name_and_out; /* * If IS_ROOT, we have to do no walking at all. */ if (IS_ROOT(entry)) { *(path++) = '\\'; if (name != NULL) goto name_and_out; goto out; } /* * Build the path string walking the tree backward from end to ROOT * and store it in reversed order [see reverse_string()] */ for (;;) { if (entry->d_name.len > SMB_MAXNAMELEN) return -ENAMETOOLONG; if (path - buf + entry->d_name.len > SMB_MAXPATHLEN) return -ENAMETOOLONG; len = server->convert(path, SMB_MAXNAMELEN, entry->d_name.name, entry->d_name.len, server->local_nls, server->remote_nls); reverse_string(path, len); path += len; *(path++) = '\\'; entry = entry->d_parent; if (IS_ROOT(entry)) break; } reverse_string(buf, path-buf);test_name_and_out: if (name != NULL) { *(path++) = '\\';name_and_out: len = server->convert(path, SMB_MAXNAMELEN, name->name, name->len, server->local_nls, server->remote_nls); path += len; }out: *(path++) = '\0'; return (path-buf);}static int smb_encode_path(struct smb_sb_info *server, char *buf, struct dentry *dir, struct qstr *name){ int result; result = smb_build_path(server, buf, dir, name); if (result < 0) goto out; if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) str_upper(buf, result);out: return result;}/* The following are taken directly from msdos-fs *//* Linear day numbers of the respective 1sts in non-leap years. */static int day_n[] ={0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */static time_tutc2local(struct smb_sb_info *server, time_t time){ return time - server->opt.serverzone*60;}static time_tlocal2utc(struct smb_sb_info *server, time_t time){ return time + server->opt.serverzone*60;}/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */static time_tdate_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time){ int month, year; time_t secs; month = ((date >> 5) & 15) - 1; year = date >> 9; secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); /* days since 1.1.70 plus 80's leap day */ return local2utc(server, secs);}/* Convert linear UNIX date to a MS-DOS time/date pair. */static voiddate_unix2dos(struct smb_sb_info *server, int unix_date, __u16 *date, __u16 *time){ int day, year, nl_day, month; unix_date = utc2local(server, unix_date); *time = (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) + (((unix_date / 3600) % 24) << 11); day = unix_date / 86400 - 3652; year = day / 365; if ((year + 3) / 4 + 365 * year > day) year--; day -= (year + 3) / 4 + 365 * year; if (day == 59 && !(year & 3)) { nl_day = day; month = 2; } else { nl_day = (year & 3) || day <= 59 ? day : day - 1; for (month = 0; month < 12; month++) if (day_n[month] > nl_day) break; } *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9);}/*****************************************************************************//* *//* Support section. *//* *//*****************************************************************************/__u32smb_len(__u8 * p){ return ((*(p+1) & 0x1) << 16L) | (*(p+2) << 8L) | *(p+3);}static __u16smb_bcc(__u8 * packet){ int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(__u16); return WVAL(packet, pos);}/* smb_valid_packet: We check if packet fulfills the basic requirements of a smb packet */static intsmb_valid_packet(__u8 * packet){ return (packet[4] == 0xff && packet[5] == 'S' && packet[6] == 'M' && packet[7] == 'B' && (smb_len(packet) + 4 == SMB_HEADER_LEN + SMB_WCT(packet) * 2 + SMB_BCC(packet)));}/* smb_verify: We check if we got the answer we expected, and if we got enough data. If bcc == -1, we don't care. */static intsmb_verify(__u8 * packet, int command, int wct, int bcc){ if (SMB_CMD(packet) != command) goto bad_command; if (SMB_WCT(packet) < wct) goto bad_wct; if (bcc != -1 && SMB_BCC(packet) < bcc) goto bad_bcc; return 0;bad_command: printk(KERN_ERR "smb_verify: command=%x, SMB_CMD=%x??\n", command, SMB_CMD(packet)); goto fail;bad_wct: printk(KERN_ERR "smb_verify: command=%x, wct=%d, SMB_WCT=%d??\n", command, wct, SMB_WCT(packet)); goto fail;bad_bcc: printk(KERN_ERR "smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n", command, bcc, SMB_BCC(packet));fail: return -EIO;}/* * Returns the maximum read or write size for the current packet size * and max_xmit value. * N.B. Since this value is usually computed before locking the server, * the server's packet size must never be decreased! */static intsmb_get_xmitsize(struct smb_sb_info *server, int overhead){ int size = server->packet_size; /* * Start with the smaller of packet size and max_xmit ... */ if (size > server->opt.max_xmit) size = server->opt.max_xmit; return size - overhead;}/* * Calculate the maximum read size */intsmb_get_rsize(struct smb_sb_info *server){ int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; int size = smb_get_xmitsize(server, overhead); VERBOSE("packet=%d, xmit=%d, size=%d\n", server->packet_size, server->opt.max_xmit, size); return size;}/* * Calculate the maximum write size */intsmb_get_wsize(struct smb_sb_info *server){ int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; int size = smb_get_xmitsize(server, overhead); VERBOSE("packet=%d, xmit=%d, size=%d\n", server->packet_size, server->opt.max_xmit, size); return size;}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: /* Why is this mapped to 0?? */ return 0; case ERRbadshare: return ETXTBSY; case ERRlock: return EDEADLK; case ERRfilexists: return EEXIST; case 87: /* should this map to 0?? */ return 0; /* Unknown error!! */ case 123: /* Invalid name?? e.g. .tmp* */ return ENOENT; case 145: /* Win NT 4.0: non-empty directory? */ return ENOTEMPTY; /* This next error seems to occur on an mv when * the destination exists */ case 183: 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; default: class = "ERRHRD"; goto err_unknown; } else if (errcls == ERRCMD) class = "ERRCMD";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_INVALID) goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -