📄 proc.c
字号:
goto out; p += result; smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) { if (smb_retry(server)) goto retry; goto out; } result = 0;out: smb_unlock_server(server); return result;}/* * Code common to mkdir and rmdir. */static intsmb_proc_generic_command(struct dentry *dentry, __u8 command){ struct smb_sb_info *server = server_from_dentry(dentry); char *p; int result; smb_lock_server(server); retry: p = smb_setup_header(server, command, 0, 0); *p++ = 4; result = smb_encode_path(server, p, dentry, NULL); if (result < 0) goto out; p += result; smb_setup_bcc(server, p); result = smb_request_ok(server, command, 0, 0); if (result < 0) { if (smb_retry(server)) goto retry; goto out; } result = 0;out: smb_unlock_server(server); return result;}intsmb_proc_mkdir(struct dentry *dentry){ return smb_proc_generic_command(dentry, SMBmkdir);}intsmb_proc_rmdir(struct dentry *dentry){ return smb_proc_generic_command(dentry, SMBrmdir);}#if SMBFS_POSIX_UNLINK/* * Removes readonly attribute from a file. Used by unlink to give posix * semantics. * Note: called with the server locked. */static intsmb_set_rw(struct dentry *dentry,struct smb_sb_info *server){ int result; struct smb_fattr fattr; /* first get current attribute */ result = smb_proc_do_getattr(server, dentry, &fattr); if (result < 0) return result; /* if RONLY attribute is set, remove it */ if (fattr.attr & aRONLY) { /* read only attribute is set */ fattr.attr &= ~aRONLY; result = smb_proc_setattr_core(server, dentry, fattr.attr); } return result;}#endifintsmb_proc_unlink(struct dentry *dentry){ struct smb_sb_info *server = server_from_dentry(dentry); int flag = 0; char *p; int result; smb_lock_server(server); retry: p = smb_setup_header(server, SMBunlink, 1, 0); WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN); *p++ = 4; result = smb_encode_path(server, p, dentry, NULL); if (result < 0) goto out; p += result; smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) {#if SMBFS_POSIX_UNLINK if (result == -EACCES && !flag) { /* Posix semantics is for the read-only state of a file to be ignored in unlink(). In the SMB world a unlink() is refused on a read-only file. To make things easier for unix users we try to override the files permission if the unlink fails with the right error. This introduces a race condition that could lead to a file being written by someone who shouldn't have access, but as far as I can tell that is unavoidable */ /* remove RONLY attribute and try again */ result = smb_set_rw(dentry,server); if (result == 0) { flag = 1; goto retry; } }#endif if (smb_retry(server)) goto retry; goto out; } result = 0;out: smb_unlock_server(server); return result;}intsmb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length){ char *p; int result; smb_lock_server(server); retry: p = smb_setup_header(server, SMBwrite, 5, 0); WSET(server->packet, smb_vwv0, fid); WSET(server->packet, smb_vwv1, 0); DSET(server->packet, smb_vwv2, length); WSET(server->packet, smb_vwv4, 0); *p++ = 4; *p++ = 0; smb_setup_bcc(server, p); if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) { if (smb_retry(server)) goto retry; goto out; } result = 0;out: smb_unlock_server(server); return result;}static voidsmb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr){ memset(fattr, 0, sizeof(*fattr)); fattr->f_nlink = 1; fattr->f_uid = server->mnt->uid; fattr->f_gid = server->mnt->gid; fattr->f_blksize = 512;}static voidsmb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr){ fattr->f_mode = server->mnt->file_mode; if (fattr->attr & aDIR) { fattr->f_mode = server->mnt->dir_mode; fattr->f_size = 512; } /* Check the read-only flag */ if (fattr->attr & aRONLY) fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); fattr->f_blocks = 0; if ((fattr->f_blksize != 0) && (fattr->f_size != 0)) { fattr->f_blocks = (fattr->f_size - 1) / fattr->f_blksize + 1; } return;}voidsmb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr){ smb_init_dirent(server, fattr); fattr->attr = aDIR; fattr->f_ino = 2; /* traditional root inode number */ fattr->f_mtime = CURRENT_TIME; smb_finish_dirent(server, fattr);}/* * Note that we are now returning the name as a reference to avoid * an extra copy, and that the upper/lower casing is done in place. * * Bugs Noted: * (1) Pathworks servers may pad the name with extra spaces. */static __u8 *smb_decode_dirent(struct smb_sb_info *server, __u8 *p, struct cache_dirent *entry){ int len; /* * SMB doesn't have a concept of inode numbers ... */ entry->ino = 0; p += SMB_STATUS_SIZE; /* reserved (search_status) */ entry->name = p + 9; len = strlen(entry->name); if (len > 12) len = 12; /* * Trim trailing blanks for Pathworks servers */ while (len > 2 && entry->name[len-1] == ' ') len--; entry->len = len; /* 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(entry->name, len); break; case SMB_CASE_LOWER: str_lower(entry->name, len); break; default: break; } entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN, entry->name, len, server->remote_nls, server->local_nls); entry->name = server->name_buf; DEBUG1("len=%d, name=%.*s\n", entry->len, entry->len, entry->name); return p + 22;}/* This routine is used to read in directory entries from the network. Note that it is for short directory name seeks, i.e.: protocol < SMB_PROTOCOL_LANMAN2 */static intsmb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos, void *cachep){ unsigned char *p; int result; int i, first, entries_seen, entries; int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; __u16 bcc; __u16 count; char status[SMB_STATUS_SIZE]; static struct qstr mask = { "*.*", 3, 0 }; unsigned char *last_status; VERBOSE("%s/%s, pos=%d\n", DENTRY_PATH(dir), fpos); smb_lock_server(server); /* N.B. We need to reinitialize the cache to restart */retry: smb_init_dircache(cachep); first = 1; entries = 0; entries_seen = 2; /* implicit . and .. */ while (1) { p = smb_setup_header(server, SMBsearch, 2, 0); WSET(server->packet, smb_vwv0, entries_asked); WSET(server->packet, smb_vwv1, aDIR); *p++ = 4; if (first == 1) { result = smb_encode_path(server, p, dir, &mask); if (result < 0) goto unlock_return; p += result; *p++ = 5; WSET(p, 0, 0); p += 2; first = 0; } else { *p++ = 0; *p++ = 5; WSET(p, 0, SMB_STATUS_SIZE); p += 2; memcpy(p, status, SMB_STATUS_SIZE); p += SMB_STATUS_SIZE; } smb_setup_bcc(server, p); result = smb_request_ok(server, SMBsearch, 1, -1); if (result < 0) { if ((server->rcls == ERRDOS) && (server->err == ERRnofiles)) break; if (smb_retry(server)) goto retry; goto unlock_return; } p = SMB_VWV(server->packet); count = WVAL(p, 0); if (count <= 0) break; result = -EIO; bcc = WVAL(p, 2); if (bcc != count * SMB_DIRINFO_SIZE + 3) goto unlock_return; p += 7; /* Make sure the response fits in the buffer. Fixed sized entries means we don't have to check in the decode loop. */ last_status = SMB_BUF(server->packet) + 3 + (count - 1) * SMB_DIRINFO_SIZE; if (last_status + SMB_DIRINFO_SIZE >= server->packet + server->packet_size) { printk(KERN_ERR "smb_proc_readdir_short: " "last dir entry outside buffer! " "%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status, server->packet_size, server->packet); goto unlock_return; } /* Read the last entry into the status field. */ memcpy(status, last_status, SMB_STATUS_SIZE); /* Now we are ready to parse smb directory entries. */ for (i = 0; i < count; i++) { struct cache_dirent this_ent, *entry = &this_ent; p = smb_decode_dirent(server, p, entry); if (entries_seen == 2 && entry->name[0] == '.') { if (entry->len == 1) continue; if (entry->name[1] == '.' && entry->len == 2) continue; } if (entries_seen >= fpos) { DEBUG1("fpos=%u\n", entries_seen); smb_add_to_cache(cachep, entry, entries_seen); entries++; } else { VERBOSE("skipped, seen=%d, i=%d, fpos=%d\n", entries_seen, i, fpos); } entries_seen++; } } result = entries;unlock_return: smb_unlock_server(server); return result;}/* * Interpret a long filename structure using the specified info level: * level 1 for anything below NT1 protocol * level 260 for NT1 protocol * * We return a reference to the name string to avoid copying, and perform * any needed upper/lower casing in place. * * 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, struct cache_dirent *entry, int level){ char *result; unsigned int len = 0; /* * SMB doesn't have a concept of inode numbers ... */ entry->ino = 0; switch (level) { case 1: len = *((unsigned char *) p + 22); entry->name = p + 23; result = p + 24 + len; VERBOSE("info 1 at %p, len=%d, name=%.*s\n", p, len, len, entry->name); break; case 260: result = p + WVAL(p, 0); len = DVAL(p, 60); if (len > 255) len = 255; /* NT4 null terminates */ entry->name = p + 94; if (len && entry->name[len-1] == '\0') len--; VERBOSE("info 260 at %p, len=%d, name=%.*s\n", p, len, len, entry->name); break; default: PARANOIA("Unknown info level %d\n", level); result = p + WVAL(p, 0); goto out; } switch (server->opt.case_handling) { case SMB_CASE_UPPER: str_upper(entry->name, len); break; case SMB_CASE_LOWER: str_lower(entry->name, len); break; default: break; } entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN, entry->name, len, server->remote_nls, server->local_nls); entry->name = server->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 smb_sb_info *server, struct dentry *dir, int fpos, void *cachep){ unsigned char *p; char *mask, *lastname, *param = server->temp_buf; __u16 command; int first, entries, 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 char *resp_data = NULL; unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; int ff_searchcount = 0; int ff_eos = 0; int ff_lastname = 0; int ff_dir_handle = 0; int loop_count = 0; int mask_len, i, result; static struct qstr star = { "*", 1, 0 }; /* * use info level 1 for older servers that don't do 260 */ if (server->opt.protocol < SMB_PROTOCOL_NT1) info_level = 1; smb_lock_server(server);retry: /* * Encode the initial path */ mask = param + 12; mask_len = smb_encode_path(server, mask, dir, &star); if (mask_len < 0) { entries = mask_len; goto unlock_return; } first = 1; VERBOSE("starting fpos=%d, mask=%s\n", fpos, mask); /* * We must reinitialize the dircache when retrying. */ smb_init_dircache(cachep); entries = 0; 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"); entries = -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); 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); } result = smb_trans2_request(server, command, 0, NULL, 12 + mask_len + 1, param, &resp_data_len, &resp_data, &resp_param_len, &resp_param); if (result < 0) { if (smb_retry(server)) { PARANOIA("error=%d, retrying\n", result); goto retry; } PARANOIA("error=%d, breaking\n", result); entries = result; break; } if (server->rcls == ERRSRV && server->err == ERRerror) { /* a damn Win95 bug - sometimes it clags if you ask it too fast */ current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/5); continue; } if (server->rcls != 0) { PARANOIA("name=%s, entries=%d, rcls=%d, err=%d\n", mask, entries, server->rcls, server->err); entries = -smb_errno(server); break; } /* parse out some important return info */ if (first != 0) { ff_dir_handle = WVAL(resp_param, 0); ff_searchcount = WVAL(resp_param, 2); ff_eos = WVAL(resp_param, 4); ff_lastname = WVAL(resp_param, 8); } else { ff_searchcount = WVAL(resp_param, 0); ff_eos = WVAL(resp_param, 2); ff_lastname = WVAL(resp_param, 6); } if (ff_searchcount == 0) break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -