📄 proc.c
字号:
/* * 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; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, PAGE_SIZE))) goto out; p = smb_setup_header(req, command, 0, 0); result = smb_simple_encode_path(req, &p, dentry, NULL); if (result < 0) goto out_free; smb_setup_bcc(req, p); result = smb_request_ok(req, command, 0, 0); if (result < 0) goto out_free; result = 0;out_free: smb_rput(req);out: 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. */static intsmb_set_rw(struct dentry *dentry,struct smb_sb_info *server){ int result; struct smb_fattr fattr; /* FIXME: cifsUE should allow removing a readonly file. */ /* first get current attribute */ smb_init_dirent(server, &fattr); result = server->ops->getattr(server, dentry, &fattr); smb_finish_dirent(server, &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; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, PAGE_SIZE))) goto out; retry: p = smb_setup_header(req, SMBunlink, 1, 0); WSET(req->rq_header, smb_vwv0, aSYSTEM | aHIDDEN); result = smb_simple_encode_path(req, &p, dentry, NULL); if (result < 0) goto out_free; smb_setup_bcc(req, p); if ((result = smb_request_ok(req, 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; req->rq_flags = 0; goto retry; } }#endif goto out_free; } result = 0;out_free: smb_rput(req);out: return result;}intsmb_proc_flush(struct smb_sb_info *server, __u16 fileid){ int result; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, 0))) goto out; smb_setup_header(req, SMBflush, 1, 0); WSET(req->rq_header, smb_vwv0, fileid); req->rq_flags |= SMB_REQ_NORETRY; result = smb_request_ok(req, SMBflush, 0, 0); smb_rput(req);out: return result;}static intsmb_proc_trunc32(struct inode *inode, loff_t length){ /* * Writing 0bytes is old-SMB magic for truncating files. * MAX_NON_LFS should prevent this from being called with a too * large offset. */ return smb_proc_write(inode, length, 0, NULL);}static intsmb_proc_trunc64(struct inode *inode, loff_t length){ struct smb_sb_info *server = server_from_inode(inode); int result; char *param; char *data; struct smb_request *req; result = -ENOMEM; if (! (req = smb_alloc_request(server, 14))) goto out; param = req->rq_buffer; data = req->rq_buffer + 6; /* FIXME: must we also set allocation size? winNT seems to do that */ WSET(param, 0, SMB_I(inode)->fileid); WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO); WSET(param, 4, 0); LSET(data, 0, length); req->rq_trans2_command = TRANSACT2_SETFILEINFO; req->rq_ldata = 8; req->rq_data = data; req->rq_lparm = 6; req->rq_parm = param; req->rq_flags |= SMB_REQ_NORETRY; result = smb_add_request(req); if (result < 0) goto out_free; result = 0; if (req->rq_rcls != 0) result = smb_errno(req);out_free: smb_rput(req);out: return result;}static intsmb_proc_trunc95(struct inode *inode, loff_t length){ struct smb_sb_info *server = server_from_inode(inode); int result = smb_proc_trunc32(inode, length); /* * win9x doesn't appear to update the size immediately. * It will return the old file size after the truncate, * confusing smbfs. So we force an update. * * FIXME: is this still necessary? */ smb_proc_flush(server, SMB_I(inode)->fileid); 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_unix = 0;}static voidsmb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr){ if (fattr->f_unix) return; fattr->f_mode = server->mnt->file_mode; if (fattr->attr & aDIR) { fattr->f_mode = server->mnt->dir_mode; fattr->f_size = SMB_ST_BLKSIZE; } /* Check the read-only flag */ if (fattr->attr & aRONLY) fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); /* How many 512 byte blocks do we need for this file? */ fattr->f_blocks = 0; if (fattr->f_size != 0) fattr->f_blocks = 1 + ((fattr->f_size-1) >> 9); return;}voidsmb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr, struct super_block *sb){ smb_init_dirent(server, fattr); fattr->attr = aDIR; fattr->f_ino = 2; /* traditional root inode number */ fattr->f_mtime = current_fs_time(sb); smb_finish_dirent(server, fattr);}/* * Decode a dirent for old protocols * * qname is filled with the decoded, and possibly translated, name. * fattr receives decoded attributes * * Bugs Noted: * (1) Pathworks servers may pad the name with extra spaces. */static char *smb_decode_short_dirent(struct smb_sb_info *server, char *p, struct qstr *qname, struct smb_fattr *fattr, unsigned char *name_buf){ int len; /* * SMB doesn't have a concept of inode numbers ... */ smb_init_dirent(server, fattr); fattr->f_ino = 0; /* FIXME: do we need this? */ p += SMB_STATUS_SIZE; /* reserved (search_status) */ fattr->attr = *p; fattr->f_mtime.tv_sec = date_dos2unix(server, WVAL(p, 3), WVAL(p, 1)); fattr->f_mtime.tv_nsec = 0; fattr->f_size = DVAL(p, 5); fattr->f_ctime = fattr->f_mtime; fattr->f_atime = fattr->f_mtime; qname->name = p + 9; len = strnlen(qname->name, 12); /* * Trim trailing blanks for Pathworks servers */ while (len > 2 && qname->name[len-1] == ' ') len--; 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. It kills const. 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; }#endif qname->len = 0; len = server->ops->convert(name_buf, SMB_MAXNAMELEN, qname->name, len, server->remote_nls, server->local_nls); if (len > 0) { qname->len = len; qname->name = name_buf; DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->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 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; 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 = { .name = "*.*", .len = 3, }; unsigned char *last_status; struct smb_request *req; unsigned char *name_buf; VERBOSE("%s/%s\n", DENTRY_PATH(dir)); lock_kernel(); result = -ENOMEM; if (! (name_buf = kmalloc(SMB_MAXNAMELEN, GFP_KERNEL))) goto out; first = 1; entries = 0; entries_seen = 2; /* implicit . and .. */ result = -ENOMEM; if (! (req = smb_alloc_request(server, server->opt.max_xmit))) goto out_name; while (1) { p = smb_setup_header(req, SMBsearch, 2, 0); WSET(req->rq_header, smb_vwv0, entries_asked); WSET(req->rq_header, smb_vwv1, aDIR); if (first == 1) { result = smb_simple_encode_path(req, &p, dir, &mask); if (result < 0) goto out_free; if (p + 3 > (char *)req->rq_buffer + req->rq_bufsize) { result = -ENAMETOOLONG; goto out_free; } *p++ = 5; WSET(p, 0, 0); p += 2; first = 0; } else { if (p + 5 + SMB_STATUS_SIZE > (char *)req->rq_buffer + req->rq_bufsize) { result = -ENAMETOOLONG; goto out_free; } *p++ = 4; *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(req, p); result = smb_request_ok(req, SMBsearch, 1, -1); if (result < 0) { if ((req->rq_rcls == ERRDOS) && (req->rq_err == ERRnofiles)) break; goto out_free; } count = WVAL(req->rq_header, smb_vwv0); if (count <= 0) break; result = -EIO; bcc = smb_bcc(req->rq_header); if (bcc != count * SMB_DIRINFO_SIZE + 3) goto out_free; p = req->rq_buffer + 3; /* Make sure the response fits in the buffer. Fixed sized entries means we don't have to check in the decode loop. */ last_status = req->rq_buffer + 3 + (count-1) * SMB_DIRINFO_SIZE; if (last_status + SMB_DIRINFO_SIZE >= req->rq_buffer + req->rq_bufsize) { printk(KERN_ERR "smb_proc_readdir_short: " "last dir entry outside buffer! " "%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status, req->rq_bufsize, req->rq_buffer); goto out_free; } /* 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++) { p = smb_decode_short_dirent(server, p, &qname, &fattr, name_buf); if (qname.len == 0) continue; 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++; } } result = entries;out_free: smb_rput(req);out_name: kfree(name_buf);out: unlock_kernel(); return result;}static void smb_decode_unix_basic(struct smb_fattr *fattr, struct smb_sb_info *server, char *p){ u64 size, disk_bytes; /* FIXME: verify nls support. all is sent as utf8? */ fattr->f_unix = 1; fattr->f_mode = 0; /* FIXME: use the uniqueID from the remote instead? */ /* 0 L file size in bytes */ /* 8 L file size on disk in bytes (block count) */ /* 40 L uid */ /* 48 L gid */ /* 56 W file type */ /* 60 L devmajor */ /* 68 L devminor */ /* 76 L unique ID (inode) */ /* 84 L permissions */ /* 92 L link count */ size = LVAL(p, 0); disk_bytes = LVAL(p, 8); /* * Some samba versions round up on-disk byte usage * to 1MB boundaries, making it useless. When seeing * that, use the size instead. */ if (!(disk_bytes & 0xfffff)) disk_bytes = size+511; fattr->f_size = size; fattr->f_blocks = disk_bytes >> 9; fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16)); fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24)); fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32)); if (server->mnt->flags & SMB_MOUNT_UID) fattr->f_uid = server->mnt->uid; else fattr->f_uid = LVAL(p, 40); if (server->mnt->flags & SMB_MOUNT_GID) fattr->f_gid = server->mnt->gid; else fattr->f_gid = LVAL(p, 48); fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56)); if (S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) { __u64 major = LVAL(p, 60); __u64 minor = LVAL(p, 68); fattr->f_rdev = MKDEV(major & 0xffffffff, minor & 0xffffffff); if (MAJOR(fattr->f_rdev) != (major & 0xffffffff) || MINOR(fattr->f_rdev) != (minor & 0xffffffff)) fattr->f_rdev = 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -