?? proc.c
字號:
/* * proc.c * * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke * * 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per */#include <linux/config.h>#include <linux/fs.h>#include <linux/smbno.h>#include <linux/smb_fs.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/malloc.h>#include <linux/stat.h>#include <linux/fcntl.h>#include <asm/segment.h>#include <asm/string.h>#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)#define SMB_CMD(packet) (BVAL(packet,8))#define SMB_WCT(packet) (BVAL(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 int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);static inline intmin(int a, int b){ return a < b ? a : b;}static voidstr_upper(char *name){ while (*name) { if (*name >= 'a' && *name <= 'z') *name -= ('a' - 'A'); name++; }}static voidstr_lower(char *name){ while (*name) { if (*name >= 'A' && *name <= 'Z') *name += ('a' - 'A'); name++; }}/*****************************************************************************//* *//* Encoding/Decoding section *//* *//*****************************************************************************/static inline byte *smb_decode_word(byte * p, word * data){ *data = WVAL(p, 0); return p + 2;}byte *smb_encode_smb_length(byte * p, dword len){ BSET(p, 0, 0); BSET(p, 1, 0); BSET(p, 2, (len & 0xFF00) >> 8); BSET(p, 3, (len & 0xFF)); if (len > 0xFFFF) { BSET(p, 1, 1); } return p + 4;}static byte *smb_encode_ascii(byte * p, const byte * name, int len){ *p++ = 4; strcpy(p, name); return p + len + 1;}static byte *smb_encode_this_name(byte * p, const char *name, const int len){ *p++ = '\\'; strncpy(p, name, len); return p + len;}/* I put smb_encode_parents into a separate function so that the recursion only takes 16 bytes on the stack per path component on a 386. */static byte *smb_encode_parents(byte * p, struct smb_inode_info *ino){ byte *q; if (ino->dir == NULL) { return p; } q = smb_encode_parents(p, ino->dir); if (q - p + 1 + ino->finfo.len > SMB_MAXPATHLEN) { return p; } return smb_encode_this_name(q, ino->finfo.name, ino->finfo.len);}static byte *smb_encode_path(struct smb_server *server, byte * p, struct smb_inode_info *dir, const char *name, const int len){ byte *start = p; if (dir != NULL) { p = smb_encode_parents(p, dir); } p = smb_encode_this_name(p, name, len); *p++ = 0; if (server->protocol <= PROTOCOL_COREPLUS) { str_upper(start); } return p;}static byte *smb_decode_data(byte * p, byte * data, word * data_len, int fs){ word len; if (!(*p == 1 || *p == 5)) { printk("smb_decode_data: Warning! Data block not starting " "with 1 or 5\n"); } len = WVAL(p, 1); p += 3; if (fs) memcpy_tofs(data, p, len); else memcpy(data, p, len); *data_len = len; return p + len;}static byte *smb_name_mangle(byte * p, const byte * name){ int len, pad = 0; len = strlen(name); if (len < 16) pad = 16 - len; *p++ = 2 * (len + pad); while (*name) { *p++ = (*name >> 4) + 'A'; *p++ = (*name & 0x0F) + 'A'; name++; } while (pad--) { *p++ = 'C'; *p++ = 'A'; } *p++ = '\0'; return p;}/* 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 */extern struct timezone sys_tz;static intutc2local(int time){ return time - sys_tz.tz_minuteswest * 60;}static intlocal2utc(int time){ return time + sys_tz.tz_minuteswest * 60;}/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */static intdate_dos2unix(unsigned short time, unsigned short date){ int month, year, 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(secs);}/*****************************************************************************//* *//* Support section. *//* *//*****************************************************************************/dwordsmb_len(byte * p){ return ((BVAL(p, 1) & 0x1) << 16L) | (BVAL(p, 2) << 8L) | (BVAL(p, 3));}static wordsmb_bcc(byte * packet){ int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word); return WVAL(packet, pos);}/* smb_valid_packet: We check if packet fulfills the basic requirements of a smb packet */static intsmb_valid_packet(byte * packet){ DDPRINTK("len: %d, wct: %d, bcc: %d\n", smb_len(packet), SMB_WCT(packet), SMB_BCC(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(byte * packet, int command, int wct, int bcc){ return (SMB_CMD(packet) == command && SMB_WCT(packet) >= wct && (bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;}static intsmb_errno(int errcls, int error){ if (errcls == ERRDOS) switch (error) { case ERRbadfunc: return EINVAL; case ERRbadfile: return ENOENT; 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: return EREMOTEIO; 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 0; case ERRbadshare: return ETXTBSY; case ERRlock: return EDEADLK; case ERRfilexists: return EEXIST; case 87: return 0; /* Unknown error!! */ /* This next error seems to occur on an mv when * the destination exists */ case 183: return EEXIST; default: return EIO; } else if (errcls == ERRSRV) switch (error) { case ERRerror: return ENFILE; case ERRbadpw: return EINVAL; case ERRbadtype: return EIO; case ERRaccess: return EACCES; default: return EIO; } else if (errcls == ERRHRD) switch (error) { case ERRnowrite: return EROFS; case ERRbadunit: return ENODEV; case ERRnotready: return EUCLEAN; case ERRbadcmd: return EIO; case ERRdata: return EIO; case ERRbadreq: return ERANGE; case ERRbadshare: return ETXTBSY; case ERRlock: return EDEADLK; default: return EIO; } else if (errcls == ERRCMD) return EIO; return 0;}static voidsmb_lock_server(struct smb_server *server){ while (server->lock) sleep_on(&server->wait); server->lock = 1;}static voidsmb_unlock_server(struct smb_server *server){ if (server->lock != 1) { printk("smb_unlock_server: was not locked!\n"); } server->lock = 0; wake_up(&server->wait);}/* 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_server *s, int command, int wct, int bcc){ int result = 0; s->rcls = 0; s->err = 0; if (smb_request(s) < 0) { DPRINTK("smb_request failed\n"); result = -EIO; } else if (smb_valid_packet(s->packet) != 0) { DPRINTK("not a valid packet!\n"); result = -EIO; } else if (s->rcls != 0) { result = -smb_errno(s->rcls, s->err); } else if (smb_verify(s->packet, command, wct, bcc) != 0) { DPRINTK("smb_verify failed\n"); result = -EIO; } return result;}/* 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. */static intsmb_retry(struct smb_server *server){ if (server->state != CONN_INVALID) { return 0; } if (smb_release(server) < 0) { DPRINTK("smb_retry: smb_release failed\n"); server->state = CONN_RETRIED; return 0; } if (smb_proc_reconnect(server) < 0) { DPRINTK("smb_proc_reconnect failed\n"); server->state = CONN_RETRIED; return 0; } server->state = CONN_VALID; return 1;}static intsmb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc){ int result = smb_request_ok(s, command, wct, bcc); smb_unlock_server(s); return result;}/* smb_setup_header: We completely set up the packet. You only have to insert the command-specific fields */__u8 *smb_setup_header(struct smb_server * server, byte command, word wct, word bcc){ dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2; byte *p = server->packet; byte *buf = server->packet; p = smb_encode_smb_length(p, xmit_len - 4); BSET(p, 0, 0xff); BSET(p, 1, 'S'); BSET(p, 2, 'M'); BSET(p, 3, 'B'); BSET(p, 4, command); p += 5; memset(p, '\0', 19); p += 19; p += 8; WSET(buf, smb_tid, server->tid); WSET(buf, smb_pid, server->pid); WSET(buf, smb_uid, server->server_uid); WSET(buf, smb_mid, server->mid); if (server->protocol > PROTOCOL_CORE) { BSET(buf, smb_flg, 0x8); WSET(buf, smb_flg2, 0x3); } *p++ = wct; /* wct */ p += 2 * wct; WSET(p, 0, bcc); return p + 2;}/* smb_setup_header_exclusive waits on server->lock and locks the server, when it's free. You have to unlock it manually when you're finished with server->packet! */static byte *smb_setup_header_exclusive(struct smb_server *server, byte command, word wct, word bcc){ smb_lock_server(server); return smb_setup_header(server, command, wct, bcc);}static voidsmb_setup_bcc(struct smb_server *server, byte * 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);}/*****************************************************************************//* *//* File operation section. *//* *//*****************************************************************************/intsmb_proc_open(struct smb_server *server, struct smb_inode_info *dir, const char *name, int len, struct smb_dirent *entry){ int error; char *p; char *buf; const word o_attr = aSYSTEM | aHIDDEN | aDIR; DPRINTK("smb_proc_open: name=%s\n", name); smb_lock_server(server); if (entry->opened != 0) { /* Somebody else opened the file while we slept */ smb_unlock_server(server); return 0; } retry: buf = server->packet; p = smb_setup_header(server, SMBopen, 2, 0); WSET(buf, smb_vwv0, 0x42); /* read/write */ WSET(buf, smb_vwv1, o_attr); *p++ = 4; p = smb_encode_path(server, p, dir, name, len); smb_setup_bcc(server, p); if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) { if (smb_retry(server)) { goto retry; } if ((error != -EACCES) && (error != -ETXTBSY) && (error != -EROFS)) { smb_unlock_server(server); return error; } /* N.B. Packet may change after request */ buf = server->packet; p = smb_setup_header(server, SMBopen, 2, 0); WSET(buf, smb_vwv0, 0x40); /* read only */ WSET(buf, smb_vwv1, o_attr); *p++ = 4; p = smb_encode_path(server, p, dir, name, len); smb_setup_bcc(server, p); if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) { if (smb_retry(server)) { goto retry; } smb_unlock_server(server); return error; } } /* We should now have data in vwv[0..6]. */ /* N.B. Packet may change after request */ buf = server->packet; entry->fileid = WVAL(buf, smb_vwv0); entry->attr = WVAL(buf, smb_vwv1); entry->f_ctime = entry->f_atime = entry->f_mtime = local2utc(DVAL(buf, smb_vwv2)); entry->f_size = DVAL(buf, smb_vwv4); entry->access = WVAL(buf, smb_vwv6); entry->opened = 1; entry->access &= 3; smb_unlock_server(server); DPRINTK("smb_proc_open: entry->access = %d\n", entry->access); return 0;}intsmb_proc_close(struct smb_server *server, __u16 fileid, __u32 mtime){ char *buf; smb_setup_header_exclusive(server, SMBclose, 3, 0); buf = server->packet; WSET(buf, smb_vwv0, fileid); DSET(buf, smb_vwv1, utc2local(mtime)); return smb_request_ok_unlock(server, SMBclose, 0, 0);}/* In smb_proc_read and smb_proc_write we do not retry, because the file-id would not be valid after a reconnection. *//* smb_proc_read: fs indicates if it should be copied with memcpy_tofs. */intsmb_proc_read(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int fs){ word returned_count, data_len; char *buf; int error;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -