diff -urN 2.0.28/fs/smbfs/Makefile 2.0.28-unix/fs/smbfs/Makefile --- 2.0.28/fs/smbfs/Makefile Wed Sep 18 16:41:46 1996 +++ 2.0.28-unix/fs/smbfs/Makefile Fri Feb 21 17:45:55 1997 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := smbfs.o -O_OBJS := proc.o sock.o inode.o file.o dir.o ioctl.o mmap.o +O_OBJS := proc.o sock.o inode.o file.o dir.o ioctl.o mmap.o symlink.o M_OBJS := $(O_TARGET) # If you want debugging output, please uncomment the following line diff -urN 2.0.28/fs/smbfs/dir.c 2.0.28-unix/fs/smbfs/dir.c --- 2.0.28/fs/smbfs/dir.c Thu Dec 5 16:48:16 1996 +++ 2.0.28-unix/fs/smbfs/dir.c Mon Feb 24 16:58:12 1997 @@ -3,6 +3,9 @@ * * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke * + * UN*X-Extensions by Linux-Team of the University of Regensburg + * 1997 + * */ #include @@ -39,9 +42,16 @@ smb_mkdir(struct inode *dir, const char *name, int len, int mode); static int + smb_mknod(struct inode *dir, const char *name, int len, int mode, int rdev); + +static int smb_rmdir(struct inode *dir, const char *name, int len); static int + smb_symlink(struct inode *dir, const char *name, int len, + const char *symname); + +static int smb_unlink(struct inode *dir, const char *name, int len); static int @@ -70,10 +80,10 @@ smb_lookup, /* lookup */ NULL, /* link */ smb_unlink, /* unlink */ - NULL, /* symlink */ + smb_symlink, /* symlink */ smb_mkdir, /* mkdir */ smb_rmdir, /* rmdir */ - NULL, /* mknod */ + smb_mknod, /* mknod */ smb_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ @@ -723,14 +733,72 @@ iput(dir); return error; } + if (SMB_SERVER(dir)->blkmode & CAP_UNIX) + { + entry.f_mode = (entry.f_mode & 0xfe00) | (mode & 0x1ff); + if ((error = smb_proc_setattr(SMB_SERVER(dir), SMB_INOP(dir), + name, len, &entry)) < 0) + { + iput(dir); + return error; + } else + { + (*result)->i_mode = entry.f_mode; + } + } iput(dir); return 0; } static int +smb_mknod(struct inode *dir, const char *name, int len, int mode, int rdev) +{ +#if 1 + return -EPERM; +#else + struct smb_dirent entry; + int error; + char *path = NULL; + + if (SMB_SERVER(dir)->protocol < PROTOCOL_LINUX1) + { + return -EPERM; + } + if (!dir || !S_ISDIR(dir->i_mode)) + { + printk("smb_mknod: inode is NULL or not a directory\n"); + iput(dir); + return -ENOENT; + } + if (len > SMB_MAXNAMELEN) + { + iput(dir); + return -ENAMETOOLONG; + } + /* Now we will have to build up an SMB filename. */ + if ((error = get_pname(dir, name, len, &path, &len)) < 0) + { + iput(dir); + return error; + } + entry.perm = mode; + entry.uid = entry.gid = (unsigned) -1; + entry.size = (unsigned) -1; + error = smb_proc_mknod(SMB_SERVER(dir), path, len, &entry); + put_pname(path); + iput(dir); + printk("smb_mknod: error=%i\n", error); + return error; +#endif +} + +static int smb_mkdir(struct inode *dir, const char *name, int len, int mode) { int error; + struct smb_dirent entry; + + if (!dir || !S_ISDIR(dir->i_mode)) { @@ -741,6 +809,21 @@ { smb_invalid_dir_cache(dir->i_ino); } + if (SMB_SERVER(dir)->blkmode & CAP_UNIX) + { + if ((error = smb_proc_getattr(dir, name, len, &entry)) < 0) + { + iput(dir); + return error; + } + entry.f_mode = (entry.f_mode & 0xfe00) | (mode & 0x1ff); + if ((error = smb_proc_setattr(SMB_SERVER(dir), SMB_INOP(dir), + name, len, &entry)) < 0) + { + iput(dir); + return error; + } + } iput(dir); return error; } @@ -769,6 +852,38 @@ iput(dir); return error; } + +static int +smb_symlink(struct inode *dir, const char *name, int len, + const char *symname) +{ + struct smb_dirent sattr; + int error; + + if (!dir || !S_ISDIR(dir->i_mode)) + { + printk("nfs_symlink: inode is NULL or not a directory\n"); + iput(dir); + return -ENOENT; + } + if (len > SMB_MAXNAMELEN) + { + iput(dir); + return -ENAMETOOLONG; + } + if (strlen(symname) > SMB_MAXPATHLEN) + { + iput(dir); + return -ENAMETOOLONG; + } + sattr.f_mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */ + sattr.f_uid = sattr.f_gid = sattr.f_size = (unsigned) -1; + sattr.f_atime = sattr.f_mtime = (unsigned) -1; + error = smb_proc_symlink(dir, name, symname, &sattr); + iput(dir); + return error; +} + static int smb_unlink(struct inode *dir, const char *name, int len) diff -urN 2.0.28/fs/smbfs/inode.c 2.0.28-unix/fs/smbfs/inode.c --- 2.0.28/fs/smbfs/inode.c Thu Dec 5 16:48:16 1996 +++ 2.0.28-unix/fs/smbfs/inode.c Mon Feb 24 16:58:12 1997 @@ -3,6 +3,9 @@ * * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke * + * UN*X-Extensions by Linux-Team of the University of Regensburg + * 1997 + * */ #include @@ -83,6 +86,18 @@ } else if (S_ISDIR(inode->i_mode)) { inode->i_op = &smb_dir_inode_operations; + } else if (S_ISLNK(inode->i_mode)) + { + inode->i_op = &smb_symlink_inode_operations; + } else if (S_ISCHR(inode->i_mode)) + { + inode->i_op = &chrdev_inode_operations; + } else if (S_ISBLK(inode->i_mode)) + { + inode->i_op = &blkdev_inode_operations; + } else if (S_ISFIFO(inode->i_mode)) + { + init_fifo(inode); } else { inode->i_op = NULL; @@ -102,7 +117,13 @@ } if (finfo->opened != 0) { - if (smb_proc_close(server, finfo->fileid, inode->i_mtime)) + __u32 mtime = inode->i_mtime; + + if (finfo->access == O_RDONLY) + { + mtime = 0xffffffff; + } + if (smb_proc_close(server, finfo->fileid, mtime)) { /* We can't do anything but complain. */ DPRINTK("smb_put_inode: could not close\n"); @@ -309,9 +330,11 @@ goto fail; } MOD_INC_USE_COUNT; + return sb; fail: + printk("smb_read_super: failed\n"); if (server->packet != NULL) { smb_vfree(server->packet); @@ -353,22 +376,27 @@ smb_notify_change(struct inode *inode, struct iattr *attr) { int error = 0; + struct smb_server *server = SMB_SERVER(inode); + if ((error = inode_change_ok(inode, attr)) < 0) return error; - if (((attr->ia_valid & ATTR_UID) && - (attr->ia_uid != SMB_SERVER(inode)->m.uid))) - return -EPERM; - - if (((attr->ia_valid & ATTR_GID) && - (attr->ia_uid != SMB_SERVER(inode)->m.gid))) - return -EPERM; - - if (((attr->ia_valid & ATTR_MODE) && - (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) - return -EPERM; - + if (!(server->blkmode & CAP_UNIX)) + { + if (((attr->ia_valid & ATTR_UID) && + (attr->ia_uid != SMB_SERVER(inode)->m.uid))) + return -EPERM; + + if (((attr->ia_valid & ATTR_GID) && + (attr->ia_gid != SMB_SERVER(inode)->m.gid))) + return -EPERM; + + if (((attr->ia_valid & ATTR_MODE) && + (attr->ia_mode & + ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) + return -EPERM; + } if ((attr->ia_valid & ATTR_SIZE) != 0) { @@ -381,41 +409,104 @@ goto fail; } - if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0) + if (!(server->blkmode & CAP_UNIX)) { + if ((attr->ia_valid & + (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0) + { - struct smb_dirent finfo; - - finfo.attr = 0; - finfo.f_size = inode->i_size; - finfo.f_blksize = inode->i_blksize; - - if ((attr->ia_valid & ATTR_CTIME) != 0) - finfo.f_ctime = attr->ia_ctime; - else - finfo.f_ctime = inode->i_ctime; + struct smb_dirent finfo; - if ((attr->ia_valid & ATTR_MTIME) != 0) - finfo.f_mtime = attr->ia_mtime; - else - finfo.f_mtime = inode->i_mtime; + finfo.attr = 0; + finfo.f_size = inode->i_size; + finfo.f_blksize = inode->i_blksize; + + if ((attr->ia_valid & ATTR_CTIME) != 0) + finfo.f_ctime = attr->ia_ctime; + else + finfo.f_ctime = inode->i_ctime; + + if ((attr->ia_valid & ATTR_MTIME) != 0) + finfo.f_mtime = attr->ia_mtime; + else + finfo.f_mtime = inode->i_mtime; + + if ((attr->ia_valid & ATTR_ATIME) != 0) + finfo.f_atime = attr->ia_atime; + else + finfo.f_atime = inode->i_atime; + printk("smb_notify_change: vor smb_proc_setattr\n"); + if ((error = smb_proc_setattr(SMB_SERVER(inode), + SMB_INOP(inode)->dir, + SMB_INOP(inode)->finfo.name, + SMB_INOP(inode)->finfo.len, + &finfo)) >= 0) + { + inode->i_ctime = finfo.f_ctime; + inode->i_mtime = finfo.f_mtime; + inode->i_atime = finfo.f_atime; + } + } + } else + { + if ((attr->ia_valid & + (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME | ATTR_UID | + ATTR_GID | ATTR_MODE)) != 0) + { - if ((attr->ia_valid & ATTR_ATIME) != 0) - finfo.f_atime = attr->ia_atime; - else - finfo.f_atime = inode->i_atime; + struct smb_dirent finfo; - if ((error = smb_proc_setattr(SMB_SERVER(inode), - inode, &finfo)) >= 0) - { - inode->i_ctime = finfo.f_ctime; - inode->i_mtime = finfo.f_mtime; - inode->i_atime = finfo.f_atime; + finfo.attr = 0; + finfo.f_size = inode->i_size; + finfo.f_blksize = inode->i_blksize; + + if ((attr->ia_valid & ATTR_MODE) != 0) + finfo.f_mode = attr->ia_mode; + else + finfo.f_mode = inode->i_mode; + + if ((attr->ia_valid & ATTR_GID) != 0) + finfo.f_gid = attr->ia_gid; + else + finfo.f_gid = inode->i_gid; + + if ((attr->ia_valid & ATTR_UID) != 0) + finfo.f_uid = attr->ia_uid; + else + finfo.f_uid = inode->i_uid; + + if ((attr->ia_valid & ATTR_CTIME) != 0) + finfo.f_ctime = attr->ia_ctime; + else + finfo.f_ctime = inode->i_ctime; + + if ((attr->ia_valid & ATTR_MTIME) != 0) + finfo.f_mtime = attr->ia_mtime; + else + finfo.f_mtime = inode->i_mtime; + + if ((attr->ia_valid & ATTR_ATIME) != 0) + finfo.f_atime = attr->ia_atime; + else + finfo.f_atime = inode->i_atime; + + if ((error = smb_proc_setattr(SMB_SERVER(inode), + SMB_INOP(inode)->dir, + SMB_INOP(inode)->finfo.name, + SMB_INOP(inode)->finfo.len, + &finfo)) >= 0) + { + inode->i_ctime = finfo.f_ctime; + inode->i_mtime = finfo.f_mtime; + inode->i_atime = finfo.f_atime; + inode->i_uid = finfo.f_uid; + inode->i_gid = finfo.f_gid; + inode->i_mode = finfo.f_mode; + } } } fail: smb_invalid_dir_cache(smb_info_ino(SMB_INOP(inode)->dir)); - return error; } diff -urN 2.0.28/fs/smbfs/proc.c 2.0.28-unix/fs/smbfs/proc.c --- 2.0.28/fs/smbfs/proc.c Tue Jan 14 18:25:44 1997 +++ 2.0.28-unix/fs/smbfs/proc.c Mon Feb 24 19:35:35 1997 @@ -4,6 +4,10 @@ * 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 + * + * UN*X-Extensions by Linux-Team of the University of Regensburg + * 1997 + * */ #include @@ -17,6 +21,7 @@ #include #include #include +#include #define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) #define SMB_CMD(packet) (BVAL(packet,8)) @@ -202,12 +207,14 @@ extern struct timezone sys_tz; + static int utc2local(int time) { return time - sys_tz.tz_minuteswest * 60; } + static int local2utc(int time) { @@ -262,13 +269,98 @@ nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9)); } +#if 0 +#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60)) +#else +#define TIME_FIXUP_CONSTANT (11644473600ULL) +#endif +/**************************************************************************** +interpret an 8 byte "filetime" structure to a time_t +It's originally in "100ns units since jan 1st 1601" +We do not have floating point in the kernel, but we have 64 bit ints!!! +****************************************************************************/ +static time_t +interpret_long_date(char *p) +{ + unsigned long long d; + time_t ret; + u32 tlow, thigh; + tlow = DVAL(p, 0); + thigh = DVAL(p, 4); -/*****************************************************************************/ -/* */ -/* Support section. */ -/* */ -/*****************************************************************************/ + if (thigh == 0) + { + return 0; + } + + d = thigh; + d <<= 32; + d += (tlow & 0xFFF00000); + + /* Ok, we have to divide by 10^7 by right shift and multiplication. */ + d >>= 7; + /* 5^7 is left */ + d = ((d * 107ULL) >> 23) + ((d * 95ULL) >> 31) + ((d * 101ULL) >> 38) + + ((d * 106ULL) >> 47) + ((d * 121ULL) >> 54); + + /* now adjust by 369 years to make the secs since 1970 */ + d -= TIME_FIXUP_CONSTANT; + + ret = (time_t) d; + + /* this takes us from kludge-GMT to real GMT */ + /* ret -= serverzone; + ret += LocTimeDiff(ret); *//* DART muss handangepasst werden */ + + return (ret); +} + + +/**************************************************************************** +put a 8 byte filetime from a time_t +This takes real GMT as input and converts to kludge-GMT +****************************************************************************/ +static void +put_long_date(char *p, time_t t) +{ + unsigned long long d; + + if (t == 0) + { + DSET(p, 0, 0); + DSET(p, 4, 0); + return; + } + /* this converts GMT to kludge-GMT */ + /* t -= TimeDiff(t) - serverzone; DART hier auch Handanpassung + */ + d = ((unsigned long long) t) + TIME_FIXUP_CONSTANT; + d *= 10000000ULL; + + DSET(p, 0, d & (0xffffffffULL)); + DSET(p, 4, d >> 32); +} + +/**************************************************************************** + change a unix mode to a dos mode +****************************************************************************/ +static int +dos_mode(umode_t mode) +{ + int result = 0; + + if ((mode & S_IWUSR) == 0) + result |= aRONLY; + + if ((mode & S_IXUSR) != 0) + result |= aARCH; + + if (S_ISDIR(mode)) + result = aDIR | (result & aRONLY); + + return (result); +} dword smb_len(byte * p) @@ -644,11 +736,17 @@ __u16 fileid, __u32 mtime) { char *buf; + __u32 send_mtime = mtime; + if ((mtime != 0) && (mtime != 0xffffffff)) + { + /* 0 and 0xffffffff mean: do not set mtime */ + send_mtime = utc2local(mtime); + } smb_setup_header_exclusive(server, SMBclose, 3, 0); buf = server->packet; WSET(buf, smb_vwv0, fileid); - DSET(buf, smb_vwv1, utc2local(mtime)); + DSET(buf, smb_vwv1, send_mtime); return smb_request_ok_unlock(server, SMBclose, 0, 0); } @@ -817,6 +915,73 @@ } int +smb_proc_symlink(struct inode *dir, const char *name, const char *symname, + struct smb_dirent *sattr) +{ + struct smb_server *server = SMB_SERVER(dir); + char param[SMB_MAXPATHLEN + 20]; + char data[SMB_MAXPATHLEN + 20]; + + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int data_len; + int resp_data_len = 0; + int resp_param_len = 0; + + int result; + char *p; + + if (!(server->blkmode & CAP_UNIX)) + { + return -EPERM; + } + WSET(param, 0, SMB_SET_FILE_UNIX_LINK); + DSET(param, 2, 0); + + p = smb_encode_path(server, param + 6, + SMB_INOP(dir), name, strlen(name)); + + data_len = strlen(symname); + + if (data_len > SMB_MAXPATHLEN) + { + data_len = SMB_MAXPATHLEN; + } + strncpy(data, symname, data_len); + + data[data_len] = 0; + data_len++; + + smb_lock_server(server); + retry: + result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, + data_len, data, + p - param, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + + if (server->rcls != 0) + { + /* + printk ("smb_proc_setattr_trans2: rcls=%d, err=%d\n", + server->rcls, server->err); + */ + smb_unlock_server(server); + return -smb_errno(server->rcls, server->err); + } + if (result < 0) + { + if (smb_retry(server)) + { + goto retry; + } + } + smb_unlock_server(server); + return 0; +} + + +int smb_proc_rmdir(struct inode *dir, const char *name, const int len) { char *p; @@ -842,6 +1007,8 @@ return result; } + + int smb_proc_unlink(struct inode *dir, const char *name, const int len) { @@ -903,7 +1070,6 @@ smb_init_dirent(struct smb_server *server, struct smb_dirent *entry) { memset(entry, 0, sizeof(struct smb_dirent)); - entry->f_nlink = 1; entry->f_uid = server->m.uid; entry->f_gid = server->m.gid; @@ -913,15 +1079,17 @@ static void smb_finish_dirent(struct smb_server *server, struct smb_dirent *entry) { - if ((entry->attr & aDIR) != 0) - { - entry->f_mode = server->m.dir_mode; - entry->f_size = 512; - } else + if (!(server->blkmode & CAP_UNIX)) { - entry->f_mode = server->m.file_mode; + if ((entry->attr & aDIR) != 0) + { + entry->f_mode = server->m.dir_mode; + entry->f_size = 512; + } else + { + entry->f_mode = server->m.file_mode; + } } - if ((entry->f_blksize != 0) && (entry->f_size != 0)) { entry->f_blocks = @@ -936,9 +1104,40 @@ void smb_init_root_dirent(struct smb_server *server, struct smb_dirent *entry) { + struct inode *dir; + struct super_block *sb; + + while ((dir = smb_kmalloc(sizeof(struct inode), GFP_KERNEL)) == NULL) + { + printk("smb_init_root_dirent: dir=NULL\n"); + schedule(); + } + + + while ((sb = smb_kmalloc(sizeof(struct super_block), GFP_KERNEL)) == NULL) + { + printk("smb_init_root_dirent: sb=NULL\n"); + schedule(); + } + smb_init_dirent(server, entry); - entry->attr = aDIR; - entry->f_ino = 1; + + if (server->blkmode & CAP_UNIX) + { + dir->u.generic_ip = NULL; + dir->i_sb = sb; + sb->u.generic_sbp = server; + smb_proc_getattr(dir, "/", 1, entry); + } else + { + entry->f_mode = server->m.dir_mode; + entry->f_size = 512; + entry->attr = aDIR; + entry->f_ino = 1; + } + smb_kfree_s(dir, sizeof(struct inode)); + smb_kfree_s(sb, sizeof(struct super_block)); + smb_finish_dirent(server, entry); } @@ -1125,12 +1324,48 @@ smb_decode_long_dirent(struct smb_server *server, char *p, struct smb_dirent *entry, int level) { + const unsigned int file_flags[6] = + {S_IFREG, S_IFDIR, S_IFLNK, + S_IFCHR, S_IFBLK, S_IFIFO}; + char *result; + u32 nxt_entr; + smb_init_dirent(server, entry); switch (level) { + case SMB_FIND_FILE_UNIX: + nxt_entr = DVAL(p, 0); + entry->len = nxt_entr - 76; + strncpy(entry->name, p + 76, entry->len); + + entry->name[entry->len] = '\0'; + entry->len = strlen(entry->name); + entry->f_size = DVAL(p, 8); + + entry->f_ctime = interpret_long_date(p + 16); + entry->f_atime = interpret_long_date(p + 24); + entry->f_mtime = interpret_long_date(p + 32); + entry->f_uid = DVAL(p, 40); + entry->f_gid = DVAL(p, 44); + entry->f_mode = (DVAL(p, 48) <= 5 ? file_flags[DVAL(p, 48)] : + file_flags[0]) | (DVAL(p, 68) & 0xfff); + if (S_ISCHR(entry->f_mode) || S_ISBLK(entry->f_mode)) + entry->f_rdev = ((DVAL(p, 52) & 0xff) << 8) | + (DVAL(p, 56) & 0xff); +/* printk ("smb_decode_long_dirent: VAL(48)=%d, VAL(68)=%xh," + "f_mode=%o\n", DVAL (p, 48), DVAL (p, 68), entry->f_mode); + */ + entry->f_nlink = DVAL(p, 72); + + entry->attr = dos_mode(entry->f_mode); + + result = p + nxt_entr; + break; + + /* We might add more levels later... */ case 1: entry->len = BVAL(p, 26); @@ -1171,7 +1406,7 @@ int cache_size, struct smb_dirent *cache) { /* NT uses 260, OS/2 uses 2. Both accept 1. */ - const int info_level = 1; + int info_level; const int max_matches = 512; char *p; @@ -1200,6 +1435,8 @@ int mask_len; unsigned char *mask = &(param[12]); + info_level = ((server->blkmode & CAP_UNIX) ? SMB_FIND_FILE_UNIX : 1); + mask_len = smb_encode_path(server, mask, SMB_INOP(dir), "*", 1) - mask; @@ -1306,6 +1543,11 @@ { switch (info_level) { + case SMB_FIND_FILE_UNIX: + lastname = p + ff_lastname; + lastname_len = resp_data_len - ff_lastname; + ff_resume_key = 0; + break; case 260: lastname = p + ff_lastname; lastname_len = resp_data_len - ff_lastname; @@ -1327,7 +1569,6 @@ for (i = 0; i < ff_searchcount; i++) { struct smb_dirent *entry = &(cache[entries]); - p = smb_decode_long_dirent(server, p, entry, info_level); @@ -1418,17 +1659,23 @@ smb_proc_getattr_trans2(struct inode *dir, const char *name, int len, struct smb_dirent *entry) { + const unsigned int file_flags[6] = + {S_IFREG, S_IFDIR, S_IFLNK, + S_IFCHR, S_IFBLK, S_IFIFO}; + struct smb_server *server = SMB_SERVER(dir); char param[SMB_MAXPATHLEN + 20]; char *p; - int result; + int result, info; unsigned char *resp_data = NULL; unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + + info = (server->blkmode & CAP_UNIX ? SMB_QUERY_FILE_UNIX_BASIC : 1); + WSET(param, 0, info); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); p = smb_encode_path(server, param + 6, SMB_INOP(dir), name, len); @@ -1458,16 +1705,46 @@ smb_unlock_server(server); return -ENOENT; } - entry->f_ctime = date_dos2unix(WVAL(resp_data, 2), - WVAL(resp_data, 0)); - entry->f_atime = date_dos2unix(WVAL(resp_data, 6), - WVAL(resp_data, 4)); - entry->f_mtime = date_dos2unix(WVAL(resp_data, 10), - WVAL(resp_data, 8)); - entry->f_size = DVAL(resp_data, 12); - entry->attr = WVAL(resp_data, 20); - smb_unlock_server(server); + switch (info) + { + case 1: + entry->f_ctime = date_dos2unix(WVAL(resp_data, 2), + WVAL(resp_data, 0)); + entry->f_atime = date_dos2unix(WVAL(resp_data, 6), + WVAL(resp_data, 4)); + entry->f_mtime = date_dos2unix(WVAL(resp_data, 10), + WVAL(resp_data, 8)); + entry->f_size = DVAL(resp_data, 12); + entry->attr = WVAL(resp_data, 20); + break; + + case SMB_QUERY_FILE_UNIX_BASIC: + entry->f_size = DVAL(resp_data, 0); + + entry->f_ctime = interpret_long_date(resp_data + 8); + entry->f_atime = interpret_long_date(resp_data + 16); + entry->f_mtime = interpret_long_date(resp_data + 24); + entry->f_uid = DVAL(resp_data, 32); + entry->f_gid = DVAL(resp_data, 36); + entry->f_mode = (DVAL(resp_data, 40) <= 5 ? + file_flags[DVAL(resp_data, 40)] : + file_flags[0]) | (DVAL(resp_data, 60) & 0xfff); + if (S_ISCHR(entry->f_mode) || S_ISBLK(entry->f_mode)) + { + entry->f_rdev = ((DVAL(resp_data, 44) & 0xff) << 8) | + (DVAL(resp_data, 48) & 0xff); + } + + entry->f_nlink = DVAL(resp_data, 64); + entry->attr = dos_mode(entry->f_mode); + break; + + default: + break; + } + + smb_unlock_server(server); return 0; } @@ -1498,11 +1775,82 @@ } +int +smb_proc_readlink(struct inode *inode, int **p0, char **string, + unsigned int *len, unsigned int maxlen) +{ + struct smb_server *server = SMB_SERVER(inode); + char param[SMB_MAXPATHLEN + 20]; + char *p; + int result; + + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + while (!(*p0 = (int *) kmalloc(maxlen, GFP_KERNEL))) + { + schedule(); + } + + *string = (char *) *p0; + + WSET(param, 0, SMB_QUERY_FILE_UNIX_LINK); /* Info level */ + DSET(param, 2, 0); + p = smb_encode_path(server, param + 6, SMB_INOP(inode)->dir, + SMB_FINFO(inode)->name, + strlen(SMB_FINFO(inode)->name)); + + smb_lock_server(server); + retry: + result = smb_trans2_request(server, TRANSACT2_QPATHINFO, + 0, NULL, + p - param, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + if (server->rcls != 0) + { + smb_unlock_server(server); + } + if (result < 0) + { + if (smb_retry(server)) + { + goto retry; + } + smb_unlock_server(server); + return result; + } +#if 0 + if (resp_data_len < 22) + { + smb_unlock_server(server); + return -ENOENT; + } +#endif + + *len = resp_data_len; + if (*len > maxlen - 1) + { + *len = maxlen - 1; + } + strncpy(*string, resp_data, *len); + *(*string + *len) = 0; + + smb_unlock_server(server); + + return 0; +} + + + /* In core protocol, there is only 1 time to be set, we use entry->f_mtime, to make touch work. */ static int smb_proc_setattr_core(struct smb_server *server, - struct inode *i, struct smb_dirent *new_finfo) + struct smb_inode_info *dir_info, const char *name, + int len, struct smb_dirent *new_finfo) { char *p; char *buf; @@ -1516,9 +1864,7 @@ WSET(buf, smb_vwv0, new_finfo->attr); DSET(buf, smb_vwv1, utc2local(new_finfo->f_mtime)); *p++ = 4; - p = smb_encode_path(server, p, - SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name, - SMB_INOP(i)->finfo.len); + p = smb_encode_path(server, p, dir_info, name, len); p = smb_encode_ascii(p, "", 0); smb_setup_bcc(server, p); @@ -1535,41 +1881,79 @@ static int smb_proc_setattr_trans2(struct smb_server *server, - struct inode *i, struct smb_dirent *new_finfo) + struct smb_inode_info *dir_info, const char *name, + int len, struct smb_dirent *new_finfo) { char param[SMB_MAXPATHLEN + 20]; - char data[26]; + char data[68]; char *p; - int result; + int result, info_level; unsigned char *resp_data = NULL; unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; - WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + + info_level = (server->blkmode & CAP_UNIX ? + SMB_QUERY_FILE_UNIX_BASIC : 1); + WSET(param, 0, info_level); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); - p = smb_encode_path(server, param + 6, - SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name, - SMB_INOP(i)->finfo.len); + p = smb_encode_path(server, param + 6, dir_info, name, len); + + if (server->blkmode & CAP_UNIX) + { + DSET(data, 0, new_finfo->f_size); + DSET(data, 4, 0); + put_long_date(data + 8, new_finfo->f_ctime); + put_long_date(data + 16, new_finfo->f_atime); + put_long_date(data + 24, new_finfo->f_mtime); + DSET(data, 32, new_finfo->f_uid); + DSET(data, 36, new_finfo->f_gid); + if (S_ISREG(new_finfo->f_mode)) + DSET(data, 40, 0); /* File */ + else if (S_ISDIR(new_finfo->f_mode)) + DSET(data, 40, 1); /* Dir */ + else if (S_ISLNK(new_finfo->f_mode)) + DSET(data, 40, 2); /* Symbolic link */ + else if (S_ISCHR(new_finfo->f_mode)) + DSET(data, 40, 3); /* Character device */ + else if (S_ISBLK(new_finfo->f_mode)) + DSET(data, 40, 4); /* Block device */ + else if (S_ISFIFO(new_finfo->f_mode)) + DSET(data, 40, 5); /* FIFO (named pipe) */ + else + printk("setattr_trans2: unknown filetype %d\n", new_finfo->f_mode); + + DSET(data, 44, major(new_finfo->f_rdev)); + DSET(data, 48, minor(new_finfo->f_rdev)); + DSET(data, 60, new_finfo->f_mode & 0xfff); + DSET(data, 64, new_finfo->f_nlink); + } else + { + date_unix2dos(new_finfo->f_ctime, &(data[0]), &(data[2])); + date_unix2dos(new_finfo->f_atime, &(data[4]), &(data[6])); + date_unix2dos(new_finfo->f_mtime, &(data[8]), &(data[10])); + DSET(data, 12, new_finfo->f_size); + DSET(data, 16, new_finfo->f_blksize); + WSET(data, 20, new_finfo->attr); + WSET(data, 22, 0); + } - date_unix2dos(new_finfo->f_ctime, &(data[0]), &(data[2])); - date_unix2dos(new_finfo->f_atime, &(data[4]), &(data[6])); - date_unix2dos(new_finfo->f_mtime, &(data[8]), &(data[10])); - DSET(data, 12, new_finfo->f_size); - DSET(data, 16, new_finfo->f_blksize); - WSET(data, 20, new_finfo->attr); - WSET(data, 22, 0); smb_lock_server(server); retry: result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, - 26, data, p - param, param, + (info_level == 1 ? 26 : 68), + data, p - param, param, &resp_data_len, &resp_data, &resp_param_len, &resp_param); if (server->rcls != 0) { + /* printk ("smb_proc_setattr_trans2: rcls=%d, err=%d\n", + server->rcls, server->err); + */ smb_unlock_server(server); return -smb_errno(server->rcls, server->err); } @@ -1585,18 +1969,20 @@ } int -smb_proc_setattr(struct smb_server *server, struct inode *inode, - struct smb_dirent *new_finfo) +smb_proc_setattr(struct smb_server *server, struct smb_inode_info *dir_info, + const char *name, int len, struct smb_dirent *new_finfo) { int result; if (server->protocol >= PROTOCOL_LANMAN2) { - result = smb_proc_setattr_trans2(server, inode, new_finfo); + result = smb_proc_setattr_trans2(server, dir_info, name, + len, new_finfo); } - if ((server->protocol < PROTOCOL_LANMAN2) || (result < 0)) + if ((server->protocol < PROTOCOL_LANMAN2) || + ((result < 0) && !(server->blkmode & CAP_UNIX))) { - result = smb_proc_setattr_core(server, inode, new_finfo); + result = smb_proc_setattr_core(server, dir_info, name, len, new_finfo); } return result; } @@ -1790,6 +2176,7 @@ server->maxvcs = WVAL(server->packet, smb_vwv2 + 1); server->blkmode = DVAL(server->packet, smb_vwv9 + 1); server->sesskey = DVAL(server->packet, smb_vwv7 + 1); + } else { server->max_xmit = WVAL(server->packet, smb_vwv2); diff -urN 2.0.28/fs/smbfs/sock.c 2.0.28-unix/fs/smbfs/sock.c --- 2.0.28/fs/smbfs/sock.c Tue Jan 14 18:25:44 1997 +++ 2.0.28-unix/fs/smbfs/sock.c Mon Feb 24 16:58:12 1997 @@ -373,7 +373,6 @@ int total_data = 0; int total_param = 0; int result; - unsigned char *inbuf = server->packet; unsigned char *rcv_buf; int buf_len; int data_len = 0; @@ -389,8 +388,8 @@ *ldata = *lparam = 0; return 0; } - total_data = WVAL(inbuf, smb_tdrcnt); - total_param = WVAL(inbuf, smb_tprcnt); + total_data = WVAL(server->packet, smb_tdrcnt); + total_param = WVAL(server->packet, smb_tprcnt); DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param); @@ -415,6 +414,8 @@ while (1) { + unsigned char *inbuf = server->packet; + if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt) > total_param) { diff -urN 2.0.28/fs/smbfs/symlink.c 2.0.28-unix/fs/smbfs/symlink.c --- 2.0.28/fs/smbfs/symlink.c Thu Jan 1 01:00:00 1970 +++ 2.0.28-unix/fs/smbfs/symlink.c Mon Feb 24 16:58:12 1997 @@ -0,0 +1,130 @@ +/* + * linux/fs/smbfs/symlink.c + * + * Copyright (C) 1992 Rick Sladkey + * + * Optimization changes Copyright (C) 1994 Florian La Roche + * + * Adapted nfs symlink handling code for smbfs + * by Linux-Team of the University of Regensburg 1997 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int smb_readlink(struct inode *, char *, int); +static int smb_follow_link(struct inode *, struct inode *, int, int, + struct inode **); + +/* + * symlinks can't do much... + */ +struct inode_operations smb_symlink_inode_operations = +{ + NULL, /* no file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + smb_readlink, /* readlink */ + smb_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + +static int +smb_follow_link(struct inode *dir, struct inode *inode, + int flag, int mode, struct inode **res_inode) +{ + int error, *mem; + unsigned int len; + char *res, *res2; + + *res_inode = NULL; + if (!dir) + { + dir = current->fs->root; + dir->i_count++; + } + if (!inode) + { + iput(dir); + return -ENOENT; + } + if (!S_ISLNK(inode->i_mode)) + { + iput(dir); + *res_inode = inode; + return 0; + } + if (current->link_count > 5) + { + iput(inode); + iput(dir); + return -ELOOP; + } + error = smb_proc_readlink(inode, &mem, &res, &len, SMB_MAXPATHLEN); + if (error) + { + iput(inode); + iput(dir); + kfree(mem); + return error; + } + while ((res2 = (char *) kmalloc(SMB_MAXPATHLEN + 1, GFP_NFS)) == NULL) + { + schedule(); + } + memcpy(res2, res, len); + res2[len] = '\0'; + kfree(mem); + iput(inode); + current->link_count++; + error = open_namei(res2, flag, mode, res_inode, dir); + current->link_count--; + kfree_s(res2, SMB_MAXPATHLEN + 1); + return error; +} + +static int +smb_readlink(struct inode *inode, char *buffer, int buflen) +{ + int error, *mem; + unsigned int len; + char *res; + + if (!S_ISLNK(inode->i_mode)) + { + iput(inode); + return -EINVAL; + } + if (buflen > SMB_MAXPATHLEN) + buflen = SMB_MAXPATHLEN; + error = smb_proc_readlink(inode, &mem, &res, &len, buflen); + + iput(inode); + if (!error) + { + memcpy_tofs(buffer, res, len); + put_user('\0', buffer + len); + error = len; + } + kfree(mem); + return error; +} diff -urN 2.0.28/include/linux/smb.h 2.0.28-unix/include/linux/smb.h --- 2.0.28/include/linux/smb.h Thu Dec 5 16:48:16 1996 +++ 2.0.28-unix/include/linux/smb.h Fri Feb 21 17:45:55 1997 @@ -3,6 +3,9 @@ * * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke * + * UN*X-Extensions by Linux-Team of the University of Regensburg + * 1997 + * */ #ifndef _LINUX_SMB_H @@ -78,6 +81,8 @@ #define LANMAN1 #define LANMAN2 #define NT1 + +#define CAP_UNIX 0x8000 enum smb_protocol { PROTOCOL_NONE, diff -urN 2.0.28/include/linux/smb_fs.h 2.0.28-unix/include/linux/smb_fs.h --- 2.0.28/include/linux/smb_fs.h Thu Dec 5 16:48:16 1996 +++ 2.0.28-unix/include/linux/smb_fs.h Mon Feb 24 16:48:59 1997 @@ -3,6 +3,9 @@ * * Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke * + * UN*X-Extensions by Linux-Team of the University of Regensburg + * 1997 + * */ #ifndef _LINUX_SMB_FS_H @@ -140,6 +143,10 @@ void smb_invalidate_all_inodes(struct smb_server *server); void smb_free_dir_cache(void); +/* linux/fs/smbfs/symlink.c */ + +extern struct inode_operations smb_symlink_inode_operations; + /* linux/fs/smbfs/ioctl.c */ int smb_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg); @@ -177,15 +184,19 @@ struct inode *ndir, const char *nname, const int nlen); int smb_proc_mkdir(struct inode *dir, const char *name, const int len); int smb_proc_rmdir(struct inode *dir, const char *name, const int len); +int smb_proc_symlink(struct inode *dir, const char *name, + const char *symname, struct smb_dirent *sattr); int smb_proc_unlink(struct inode *dir, const char *name, const int len); int smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos, int cache_size, struct smb_dirent *entry); int smb_proc_getattr(struct inode *dir, const char *name, int len, struct smb_dirent *entry); -int smb_proc_setattr(struct smb_server *server, - struct inode *ino, - struct smb_dirent *new_finfo); +int smb_proc_readlink(struct inode *inode, int **p0, char **string, + unsigned int *len, unsigned int maxlen); +int smb_proc_setattr(struct smb_server *server, + struct smb_inode_info *dir_info, + const char *name, int len, struct smb_dirent *new_finfo); int smb_proc_chkpath(struct smb_server *server, char *path, int len, int *result); int smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr); diff -urN 2.0.28/include/linux/smbno.h 2.0.28-unix/include/linux/smbno.h --- 2.0.28/include/linux/smbno.h Wed Sep 18 16:41:49 1996 +++ 2.0.28-unix/include/linux/smbno.h Fri Feb 21 17:45:55 1997 @@ -266,4 +266,12 @@ #define TRANSACT2_FINDNOTIFYNEXT 12 #define TRANSACT2_MKDIR 13 +/* INFO_LEVELS */ + +#define SMB_SET_FILE_UNIX_BASIC 0x200 +#define SMB_QUERY_FILE_UNIX_BASIC 0x200 +#define SMB_SET_FILE_UNIX_LINK 0x201 +#define SMB_QUERY_FILE_UNIX_LINK 0x201 +#define SMB_FIND_FILE_UNIX 0x202 + #endif /* _SMBNO_H_ */