📄 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/capability.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/smp_lock.h>#include <linux/net.h>#include <linux/vfs.h>#include <linux/smb_fs.h>#include <linux/smbno.h>#include <linux/smb_mount.h>#include <net/sock.h>#include <asm/string.h>#include <asm/div64.h>#include "smb_debug.h"#include "proto.h"#include "request.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_DIRINFO_SIZE 43#define SMB_STATUS_SIZE 21#define SMB_ST_BLKSIZE (PAGE_SIZE)#define SMB_ST_BLKSHIFT (PAGE_SHIFT)static struct smb_ops smb_ops_core;static struct smb_ops smb_ops_os2;static struct smb_ops smb_ops_win95;static struct smb_ops smb_ops_winNT;static struct smb_ops smb_ops_unix;static struct smb_ops smb_ops_null;static voidsmb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);static voidsmb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);static intsmb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr);static intsmb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, struct smb_fattr *fattr);static intsmb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry, u16 attr);static intsmb_proc_setattr_ext(struct smb_sb_info *server, struct inode *inode, struct smb_fattr *fattr);static intsmb_proc_query_cifsunix(struct smb_sb_info *server);static voidinstall_ops(struct smb_ops *dst, struct smb_ops *src);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(unsigned char *output, int olen, const unsigned 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;}static inline int write_char(unsigned char ch, char *output, int olen){ if (olen < 4) return -ENAMETOOLONG; sprintf(output, ":x%02x", ch); return 4;}static inline int write_unichar(wchar_t ch, char *output, int olen){ if (olen < 5) return -ENAMETOOLONG; sprintf(output, ":%04x", ch); return 5;}/* convert from one "codepage" to another (possibly being utf8). */static int convert_cp(unsigned char *output, int olen, const unsigned char *input, int ilen, struct nls_table *nls_from, struct nls_table *nls_to){ int len = 0; int n; wchar_t ch; while (ilen > 0) { /* convert by changing to unicode and back to the new cp */ n = nls_from->char2uni(input, ilen, &ch); if (n == -EINVAL) { ilen--; n = write_char(*input++, output, olen); if (n < 0) goto fail; output += n; olen -= n; len += n; continue; } else if (n < 0) goto fail; input += n; ilen -= n; n = nls_to->uni2char(ch, output, olen); if (n == -EINVAL) n = write_unichar(ch, output, olen); if (n < 0) goto fail; output += n; olen -= n; len += n; } return len;fail: return n;}/* ----------------------------------------------------------- *//* * nls_unicode * * This encodes/decodes little endian unicode format */static int uni2char(wchar_t uni, unsigned char *out, int boundlen){ if (boundlen < 2) return -EINVAL; *out++ = uni & 0xff; *out++ = uni >> 8; return 2;}static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni){ if (boundlen < 2) return -EINVAL; *uni = (rawstring[1] << 8) | rawstring[0]; return 2;}static struct nls_table unicode_table = { .charset = "unicode", .uni2char = uni2char, .char2uni = char2uni,};/* ----------------------------------------------------------- */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 && *p != &unicode_table) 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; /* local */ n = setcodepage(&server->local_nls, cp->local_name); if (n != 0) goto out; /* remote */ if (!strcmp(cp->remote_name, "unicode")) { server->remote_nls = &unicode_table; } else { 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->ops->convert = convert_cp; else server->ops->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, unsigned char *buf, int maxlen, struct dentry *entry, struct qstr *name){ unsigned char *path = buf; int len; int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE) != 0; if (maxlen < (2<<unicode)) return -ENAMETOOLONG; if (maxlen > SMB_MAXPATHLEN + 1) maxlen = SMB_MAXPATHLEN + 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++ = '\\'; if (unicode) *path++ = '\0'; *path++ = '\0'; if (unicode) *path++ = '\0'; return path-buf; } /* * Build the path string walking the tree backward from end to ROOT * and store it in reversed order [see reverse_string()] */ dget(entry); spin_lock(&entry->d_lock); while (!IS_ROOT(entry)) { struct dentry *parent; if (maxlen < (3<<unicode)) { spin_unlock(&entry->d_lock); dput(entry); return -ENAMETOOLONG; } len = server->ops->convert(path, maxlen-2, entry->d_name.name, entry->d_name.len, server->local_nls, server->remote_nls); if (len < 0) { spin_unlock(&entry->d_lock); dput(entry); return len; } reverse_string(path, len); path += len; if (unicode) { /* Note: reverse order */ *path++ = '\0'; maxlen--; } *path++ = '\\'; maxlen -= len+1; parent = entry->d_parent; dget(parent); spin_unlock(&entry->d_lock); dput(entry); entry = parent; spin_lock(&entry->d_lock); } spin_unlock(&entry->d_lock); dput(entry); reverse_string(buf, path-buf); /* maxlen has space for at least one char */test_name_and_out: if (name) { if (maxlen < (3<<unicode)) return -ENAMETOOLONG; *path++ = '\\'; if (unicode) { *path++ = '\0'; maxlen--; } len = server->ops->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 has space for at least one char */ *path++ = '\0'; if (unicode) *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;}/* encode_path for non-trans2 request SMBs */static int smb_simple_encode_path(struct smb_request *req, char **p, struct dentry * entry, struct qstr * name){ struct smb_sb_info *server = req->rq_server; char *s = *p; int res; int maxlen = ((char *)req->rq_buffer + req->rq_bufsize) - s; int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); if (!maxlen) return -ENAMETOOLONG; *s++ = 4; /* ASCII data format */ /* * SMB Unicode strings must be 16bit aligned relative the start of the * packet. If they are not they must be padded with 0. */ if (unicode) { int align = s - (char *)req->rq_buffer; if (!(align & 1)) { *s++ = '\0'; maxlen--; } } 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 struct timespecsmb_ntutc2unixutc(u64 ntutc){ struct timespec ts; /* FIXME: what about the timezone difference? */ /* Subtract the NTFS time offset, then convert to 1s intervals. */ u64 t = ntutc - NTFS_TIME_OFFSET;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -