📄 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/slab.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 <asm/div64.h>#include "smb_debug.h"#include "proto.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. */#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 21#define SMB_ST_BLKSIZE (PAGE_SIZE)#define SMB_ST_BLKSHIFT (PAGE_SHIFT)static 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 voidstr_upper(char *name, int len){ while (len--) { if (*name >= 'a' && *name <= 'z') *name -= ('a' - 'A'); name++; }}#if 0static voidstr_lower(char *name, int len){ while (len--) { if (*name >= 'A' && *name <= 'Z') *name += ('a' - 'A'); name++; }}#endif/* 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){ if (olen < ilen) return -ENAMETOOLONG; 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 fail; input += n; ilen -= n; n = nls_to->uni2char(ch, output, olen); if (n < 0) goto fail; output += n; olen -= n; len += n; } return len;fail: return n;}static int setcodepage(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 = 0; smb_lock_server(server); /* Don't load any nls_* at all, if no remote is requested */ if (!*cp->remote_name) goto out; n = setcodepage(&server->local_nls, cp->local_name); if (n != 0) goto out; n = setcodepage(&server->remote_nls, cp->remote_name); if (n != 0) setcodepage(&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 *//* *//*****************************************************************************/static __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, int maxlen, struct dentry * entry, struct qstr * name){ char *path = buf; int len; if (maxlen < 2) return -ENAMETOOLONG; if (maxlen > SMB_MAXNAMELEN + 1) maxlen = SMB_MAXNAMELEN + 1; if (entry == NULL) goto test_name_and_out; /* * If IS_ROOT, we have to do no walking at all. */ if (IS_ROOT(entry) && !name) { *path++ = '\\'; *path++ = '\0'; return 2; } /* * Build the path string walking the tree backward from end to ROOT * and store it in reversed order [see reverse_string()] */ while (!IS_ROOT(entry)) { if (maxlen < 3) return -ENAMETOOLONG; len = server->convert(path, maxlen-2, entry->d_name.name, entry->d_name.len, server->local_nls, server->remote_nls); if (len < 0) return len; reverse_string(path, len); path += len; *path++ = '\\'; maxlen -= len+1; entry = entry->d_parent; if (IS_ROOT(entry)) break; } reverse_string(buf, path-buf); /* maxlen is at least 1 */test_name_and_out: if (name) { if (maxlen < 3) return -ENAMETOOLONG; *path++ = '\\'; len = server->convert(path, maxlen-2, name->name, name->len, server->local_nls, server->remote_nls); if (len < 0) return len; path += len; maxlen -= len+1; } /* maxlen is at least 1 */ *path++ = '\0'; return path-buf;}static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen, struct dentry *dir, struct qstr *name){ int result; result = smb_build_path(server, buf, maxlen, dir, name); if (result < 0) goto out; if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) str_upper(buf, result);out: return result;}static int smb_simple_encode_path(struct smb_sb_info *server, char **p, struct dentry * entry, struct qstr * name){ char *s = *p; int res; int maxlen = ((char *)server->packet + server->packet_size) - s; if (!maxlen) return -ENAMETOOLONG; *s++ = 4; res = smb_encode_path(server, s, maxlen-1, entry, name); if (res < 0) return res; *p = s + res; return 0;}/* 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; /* first subtract and mask after that... Otherwise, if date == 0, bad things happen */ month = ((date >> 5) - 1) & 15; 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); if (unix_date < 315532800) unix_date = 315532800; *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);}/* The following are taken from fs/ntfs/util.c */#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)/* * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units) * into Unix UTC (based 1970-01-01, in seconds). */static time_tsmb_ntutc2unixutc(u64 ntutc){ /* FIXME: what about the timezone difference? */ /* Subtract the NTFS time offset, then convert to 1s intervals. */ u64 t = ntutc - NTFS_TIME_OFFSET; do_div(t, 10000000); return (time_t)t;}#if 0/* Convert the Unix UTC into NT time */static u64smb_unixutc2ntutc(struct smb_sb_info *server, time_t t){ /* Note: timezone conversion is probably wrong. */ return ((u64)utc2local(server, t)) * 10000000 + NTFS_TIME_OFFSET;}#endif/*****************************************************************************//* *//* 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 "payload". Making all of the * packet fit within the negotiated max_xmit size. * * N.B. Since this value is usually computed before locking the server, * the server's packet size must never be decreased! */static inline intsmb_get_xmitsize(struct smb_sb_info *server, int overhead){ return server->opt.max_xmit - 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -