📄 proc.c
字号:
fattr->f_mode |= LVAL(p, 84); if ( (server->mnt->flags & SMB_MOUNT_DMODE) && (S_ISDIR(fattr->f_mode)) ) fattr->f_mode = (server->mnt->dir_mode & S_IRWXUGO) | S_IFDIR; else if ( (server->mnt->flags & SMB_MOUNT_FMODE) && !(S_ISDIR(fattr->f_mode)) ) fattr->f_mode = (server->mnt->file_mode & S_IRWXUGO) | (fattr->f_mode & S_IFMT);}/* * Interpret a long filename structure using the specified info level: * level 1 for anything below NT1 protocol * level 260 for NT1 protocol * * qname is filled with the decoded, and possibly translated, name * fattr receives decoded attributes. * * Bugs Noted: * (1) Win NT 4.0 appends a null byte to names and counts it in the length! */static char *smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level, struct qstr *qname, struct smb_fattr *fattr, unsigned char *name_buf){ char *result; unsigned int len = 0; int n; __u16 date, time; int unicode = (server->mnt->flags & SMB_MOUNT_UNICODE); /* * SMB doesn't have a concept of inode numbers ... */ smb_init_dirent(server, fattr); fattr->f_ino = 0; /* FIXME: do we need this? */ switch (level) { case 1: len = *((unsigned char *) p + 22); qname->name = p + 23; result = p + 24 + len; date = WVAL(p, 0); time = WVAL(p, 2); fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); fattr->f_ctime.tv_nsec = 0; date = WVAL(p, 4); time = WVAL(p, 6); fattr->f_atime.tv_sec = date_dos2unix(server, date, time); fattr->f_atime.tv_nsec = 0; date = WVAL(p, 8); time = WVAL(p, 10); fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); fattr->f_mtime.tv_nsec = 0; fattr->f_size = DVAL(p, 12); /* ULONG allocation size */ fattr->attr = WVAL(p, 20); VERBOSE("info 1 at %p, len=%d, name=%.*s\n", p, len, len, qname->name); break; case 260: result = p + WVAL(p, 0); len = DVAL(p, 60); if (len > 255) len = 255; /* NT4 null terminates, unless we are using unicode ... */ qname->name = p + 94; if (!unicode && len && qname->name[len-1] == '\0') len--; fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8)); fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16)); fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24)); /* change time (32) */ fattr->f_size = LVAL(p, 40); /* alloc size (48) */ fattr->attr = DVAL(p, 56); VERBOSE("info 260 at %p, len=%d, name=%.*s\n", p, len, len, qname->name); break; case SMB_FIND_FILE_UNIX: result = p + WVAL(p, 0); qname->name = p + 108; len = strlen(qname->name); /* FIXME: should we check the length?? */ p += 8; smb_decode_unix_basic(fattr, server, p); VERBOSE("info SMB_FIND_FILE_UNIX at %p, len=%d, name=%.*s\n", p, len, len, qname->name); break; default: PARANOIA("Unknown info level %d\n", level); result = p + WVAL(p, 0); goto out; } smb_finish_dirent(server, fattr);#if 0 /* FIXME: These only work for ascii chars, and recent smbmount doesn't allow the flag to be set anyway. Remove? */ switch (server->opt.case_handling) { case SMB_CASE_UPPER: str_upper(qname->name, len); break; case SMB_CASE_LOWER: str_lower(qname->name, len); break; default: break; }#endif qname->len = 0; n = server->ops->convert(name_buf, SMB_MAXNAMELEN, qname->name, len, server->remote_nls, server->local_nls); if (n > 0) { qname->len = n; qname->name = name_buf; }out: return result;}/* findfirst/findnext flags */#define SMB_CLOSE_AFTER_FIRST (1<<0)#define SMB_CLOSE_IF_END (1<<1)#define SMB_REQUIRE_RESUME_KEY (1<<2)#define SMB_CONTINUE_BIT (1<<3)/* * Note: samba-2.0.7 (at least) has a very similar routine, cli_list, in * source/libsmb/clilist.c. When looking for smb bugs in the readdir code, * go there for advise. * * Bugs Noted: * (1) When using Info Level 1 Win NT 4.0 truncates directory listings * for certain patterns of names and/or lengths. The breakage pattern * is completely reproducible and can be toggled by the creation of a * single file. (E.g. echo hi >foo breaks, rm -f foo works.) */static intsmb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctl){ struct dentry *dir = filp->f_path.dentry; struct smb_sb_info *server = server_from_dentry(dir); struct qstr qname; struct smb_fattr fattr; unsigned char *p, *lastname; char *mask, *param; __u16 command; int first, entries_seen; /* Both NT and OS/2 accept info level 1 (but see note below). */ int info_level = 260; const int max_matches = 512; unsigned int ff_searchcount = 0; unsigned int ff_eos = 0; unsigned int ff_lastname = 0; unsigned int ff_dir_handle = 0; unsigned int loop_count = 0; unsigned int mask_len, i; int result; struct smb_request *req; unsigned char *name_buf; static struct qstr star = { .name = "*", .len = 1, }; lock_kernel(); /* * We always prefer unix style. Use info level 1 for older * servers that don't do 260. */ if (server->opt.capabilities & SMB_CAP_UNIX) info_level = SMB_FIND_FILE_UNIX; else if (server->opt.protocol < SMB_PROTOCOL_NT1) info_level = 1; result = -ENOMEM; if (! (name_buf = kmalloc(SMB_MAXNAMELEN+2, GFP_KERNEL))) goto out; if (! (req = smb_alloc_request(server, server->opt.max_xmit))) goto out_name; param = req->rq_buffer; /* * Encode the initial path */ mask = param + 12; result = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star); if (result <= 0) goto out_free; mask_len = result - 1; /* mask_len is strlen, not #bytes */ result = 0; first = 1; VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask); entries_seen = 2; ff_eos = 0; while (ff_eos == 0) { loop_count += 1; if (loop_count > 10) { printk(KERN_WARNING "smb_proc_readdir_long: " "Looping in FIND_NEXT??\n"); result = -EIO; break; } if (first != 0) { command = TRANSACT2_FINDFIRST; WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); WSET(param, 2, max_matches); /* max count */ WSET(param, 4, SMB_CLOSE_IF_END); WSET(param, 6, info_level); DSET(param, 8, 0); } else { command = TRANSACT2_FINDNEXT; VERBOSE("handle=0x%X, lastname=%d, mask=%.*s\n", ff_dir_handle, ff_lastname, mask_len, mask); WSET(param, 0, ff_dir_handle); /* search handle */ WSET(param, 2, max_matches); /* max count */ WSET(param, 4, info_level); DSET(param, 6, 0); WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); } req->rq_trans2_command = command; req->rq_ldata = 0; req->rq_data = NULL; req->rq_lparm = 12 + mask_len + 1; req->rq_parm = param; req->rq_flags = 0; result = smb_add_request(req); if (result < 0) { PARANOIA("error=%d, breaking\n", result); break; } if (req->rq_rcls == ERRSRV && req->rq_err == ERRerror) { /* a damn Win95 bug - sometimes it clags if you ask it too fast */ schedule_timeout_interruptible(msecs_to_jiffies(200)); continue; } if (req->rq_rcls != 0) { result = smb_errno(req); PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n", mask, result, req->rq_rcls, req->rq_err); break; } /* parse out some important return info */ if (first != 0) { ff_dir_handle = WVAL(req->rq_parm, 0); ff_searchcount = WVAL(req->rq_parm, 2); ff_eos = WVAL(req->rq_parm, 4); ff_lastname = WVAL(req->rq_parm, 8); } else { ff_searchcount = WVAL(req->rq_parm, 0); ff_eos = WVAL(req->rq_parm, 2); ff_lastname = WVAL(req->rq_parm, 6); } if (ff_searchcount == 0) break; /* Now we are ready to parse smb directory entries. */ /* point to the data bytes */ p = req->rq_data; for (i = 0; i < ff_searchcount; i++) { /* make sure we stay within the buffer */ if (p >= req->rq_data + req->rq_ldata) { printk(KERN_ERR "smb_proc_readdir_long: " "dirent pointer outside buffer! " "%p %d@%p\n", p, req->rq_ldata, req->rq_data); result = -EIO; /* always a comm. error? */ goto out_free; } p = smb_decode_long_dirent(server, p, info_level, &qname, &fattr, name_buf); /* ignore . and .. from the server */ if (entries_seen == 2 && qname.name[0] == '.') { if (qname.len == 1) continue; if (qname.name[1] == '.' && qname.len == 2) continue; } if (!smb_fill_cache(filp, dirent, filldir, ctl, &qname, &fattr)) ; /* stop reading? */ entries_seen++; } VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos); /* * We might need the lastname for continuations. * * Note that some servers (win95?) point to the filename and * others (NT4, Samba using NT1) to the dir entry. We assume * here that those who do not point to a filename do not need * this info to continue the listing. * * OS/2 needs this and talks infolevel 1. * NetApps want lastname with infolevel 260. * win2k want lastname with infolevel 260, and points to * the record not to the name. * Samba+CifsUnixExt doesn't need lastname. * * Both are happy if we return the data they point to. So we do. * (FIXME: above is not true with win2k) */ mask_len = 0; if (info_level != SMB_FIND_FILE_UNIX && ff_lastname > 0 && ff_lastname < req->rq_ldata) { lastname = req->rq_data + ff_lastname; switch (info_level) { case 260: mask_len = req->rq_ldata - ff_lastname; break; case 1: /* lastname points to a length byte */ mask_len = *lastname++; if (ff_lastname + 1 + mask_len > req->rq_ldata) mask_len = req->rq_ldata - ff_lastname - 1; break; } /* * Update the mask string for the next message. */ if (mask_len > 255) mask_len = 255; if (mask_len) strncpy(mask, lastname, mask_len); } mask_len = strnlen(mask, mask_len); VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n", mask_len, ff_lastname, req->rq_ldata, mask_len, mask); first = 0; loop_count = 0; }out_free: smb_rput(req);out_name: kfree(name_buf);out: unlock_kernel(); return result;}/* * This version uses the trans2 TRANSACT2_FINDFIRST message * to get the attribute data. * * Bugs Noted: */static intsmb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry, struct smb_fattr *fattr){ char *param, *mask; __u16 date, time; int mask_len, result; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, PAGE_SIZE))) goto out; param = req->rq_buffer; mask = param + 12; mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry,NULL); if (mask_len < 0) { result = mask_len; goto out_free; } VERBOSE("name=%s, len=%d\n", mask, mask_len); WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); WSET(param, 2, 1); /* max count */ WSET(param, 4, 1); /* close after this call */ WSET(param, 6, 1); /* info_level */ DSET(param, 8, 0); req->rq_trans2_command = TRANSACT2_FINDFIRST; req->rq_ldata = 0; req->rq_data = NULL; req->rq_lparm = 12 + mask_len; req->rq_parm = param; req->rq_flags = 0; result = smb_add_request(req); if (result < 0) goto out_free; if (req->rq_rcls != 0) { result = smb_errno(req);#ifdef SMBFS_PARANOIA if (result != -ENOENT) PARANOIA("error for %s, rcls=%d, err=%d\n", mask, req->rq_rcls, req->rq_err);#endif goto out_free; } /* Make sure we got enough data ... */ result = -EINVAL; if (req->rq_ldata < 22 || WVAL(req->rq_parm, 2) != 1) { PARANOIA("bad result for %s, len=%d, count=%d\n", mask, req->rq_ldata, WVAL(req->rq_parm, 2)); goto out_free; } /* * Decode the response into the fattr ... */ date = WVAL(req->rq_data, 0); time = WVAL(req->rq_data, 2); fattr->f_ctime.tv_sec = date_dos2unix(server, date, time); fattr->f_ctime.tv_nsec = 0; date = WVAL(req->rq_data, 4); time = WVAL(req->rq_data, 6); fattr->f_atime.tv_sec = date_dos2unix(server, date, time); fattr->f_atime.tv_nsec = 0; date = WVAL(req->rq_data, 8); time = WVAL(req->rq_data, 10); fattr->f_mtime.tv_sec = date_dos2unix(server, date, time); fattr->f_mtime.tv_nsec = 0; VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n", mask, date, time, fattr->f_mtime.tv_sec); fattr->f_size = DVAL(req->rq_data, 12); /* ULONG allocation size */ fattr->attr = WVAL(req->rq_data, 20); result = 0;out_free: smb_rput(req);out: return result;}static intsmb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr){ int result; char *p; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, PAGE_SIZE))) goto out; p = smb_setup_header(req, SMBgetatr, 0, 0); result = smb_simple_encode_path(req, &p, dir, NULL); if (result < 0) goto out_free; smb_setup_bcc(req, p); if ((result = smb_request_ok(req, SMBgetatr, 10, 0)) < 0) goto out_free; fattr->attr = WVAL(req->rq_header, smb_vwv0); fattr->f_mtime.tv_sec = local2utc(server, DVAL(req->rq_header, smb_vwv1)); fattr->f_mtime.tv_nsec = 0; fattr->f_size = DVAL(req->rq_header, smb_vwv3); fattr->f_ctime = fattr->f_mtime; fattr->f_atime = fattr->f_mtime; #ifdef SMBFS_DEBUG_TIMESTAMP printk("getattr_core: %s/%s, mtime=%ld\n", DENTRY_PATH(dir), fattr->f_mtime);#endif result = 0;out_free: smb_rput(req);out: return result;}/* * Bugs Noted: * (1) Win 95 swaps the date and time fields in the standard info level. */static intsmb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, struct smb_request *req, int infolevel){ char *p, *param; int result; param = req->rq_buffer; WSET(param, 0, infolevel); DSET(param, 2, 0); result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL); if (result < 0) goto out; p = param + 6 + result; req->rq_trans2_command = TRANSACT2_QPATHINFO; req->rq_ldata = 0; req->rq_data = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -