static void smb_put_inode(struct inode *inode) { struct smb_dirent *finfo = SMB_FINFO(inode); if (finfo->opened != 0) { /* smb_proc_close wants mtime in finfo */ finfo->mtime = inode->i_mtime; if (smb_proc_close(SMB_SERVER(inode), finfo)) { /* We can't do anything but complain. */ printk("smb_put_inode: could not close\n"); } } smb_free_inode_info(SMB_INOP(inode)); if (S_ISDIR(inode->i_mode)) { DDPRINTK("smb_put_inode: put directory %ld\n", inode->i_ino); smb_invalid_dir_cache(inode->i_ino); } clear_inode(inode); }
/* We will search the inode that belongs to this name, currently by a complete linear search through the inodes belonging to this filesystem. This has to be fixed. */ static struct smb_inode_info * smb_find_dir_inode(struct inode *parent, const char *name, int len) { struct smb_server *server = SMB_SERVER(parent); struct smb_inode_info *dir = SMB_INOP(parent); struct smb_inode_info *result = &(server->root); if (name == NULL) { return NULL; } if ((len == 1) && (name[0] == '.')) { return dir; } if ((len == 2) && (name[0] == '.') && (name[1] == '.')) { return dir->dir; } do { if (result->dir == dir) { if (compare_filename(server, name, len, &(result->finfo)) == 0) { return result; } } result = result->next; } while (result != &(server->root)); return NULL; }
int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct smb_sb_info *server = SMB_SERVER(inode); struct smb_conn_opt opt; int result = -EINVAL; switch (cmd) { case SMB_IOC_GETMOUNTUID: result = put_user(NEW_TO_OLD_UID(server->mnt->mounted_uid), (uid16_t *) arg); break; case SMB_IOC_GETMOUNTUID32: result = put_user(server->mnt->mounted_uid, (uid_t *) arg); break; case SMB_IOC_NEWCONN: /* require an argument == the mount data, else it is EINVAL */ if (!arg) break; result = -EFAULT; if (!copy_from_user(&opt, (void *)arg, sizeof(opt))) result = smb_newconn(server, &opt); break; default: } return result; }
static int smb_create(struct inode *dir, const char *name, int len, int mode, struct inode **result) { int error; struct smb_dirent entry; struct smb_inode_info *new_inode_info; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("smb_create: inode is NULL or not a directory\n"); iput(dir); return -ENOENT; } new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info), GFP_KERNEL); if (new_inode_info == NULL) { iput(dir); return -ENOMEM; } error = smb_proc_create(dir, name, len, 0, CURRENT_TIME); if (error < 0) { smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); iput(dir); return error; } smb_invalid_dir_cache(dir->i_ino); if ((error = smb_proc_getattr(dir, name, len, &entry)) < 0) { smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); iput(dir); return error; } entry.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1); new_inode_info->finfo = entry; if ((*result = smb_iget(dir, new_inode_info)) == NULL) { iput(dir); return error; } iput(dir); return 0; }
static struct inode * smb_iget(struct inode *dir, struct smb_inode_info *new_inode_info) { struct inode *inode; struct smb_inode_info *root; if ((dir == NULL) || (new_inode_info == NULL)) { printk("smb_iget: parameter is NULL\n"); return NULL; } new_inode_info->state = SMB_INODE_LOOKED_UP; new_inode_info->nused = 0; new_inode_info->dir = SMB_INOP(dir); SMB_INOP(dir)->nused += 1; /* * We have to link the new inode_info into the doubly linked * list of inode_infos to make a complete linear search possible. */ root = &(SMB_SERVER(dir)->root); new_inode_info->prev = root; new_inode_info->next = root->next; root->next->prev = new_inode_info; root->next = new_inode_info; if (!(inode = iget(dir->i_sb, smb_info_ino(new_inode_info)))) { printk("smb_iget: iget failed!"); /* * If we blocked in iget(), another task may have referenced * the info structure ... clean up with smb_free_inode_info. */ smb_free_inode_info(new_inode_info); return NULL; } return inode; }
int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct smb_sb_info *server = SMB_SERVER(inode); int result = -EINVAL; switch (cmd) { case SMB_IOC_GETMOUNTUID: result = put_user(server->mnt->mounted_uid, (uid_t *) arg); break; case SMB_IOC_NEWCONN: { struct smb_conn_opt opt; if (arg) { result = -EFAULT; if (!copy_from_user(&opt, (void *)arg, sizeof(opt))) result = smb_newconn(server, &opt); } else { #if 0 /* obsolete option ... print a warning */ printk("SMBFS: ioctl deprecated, please upgrade " "smbfs package\n"); #endif result = 0; } break; } default: } return result; }
static int smb_lookup(struct inode *dir, const char *name, int len, struct inode **result) { struct smb_dirent finfo; struct smb_inode_info *result_info; int error; int found_in_cache; struct smb_inode_info *new_inode_info = NULL; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { printk("smb_lookup: inode is NULL or not a directory.\n"); iput(dir); return -ENOENT; } DDPRINTK("smb_lookup: %s\n", name); /* Fast cheat for . */ if (len == 0 || (len == 1 && name[0] == '.')) { *result = dir; return 0; } /* ..and for .. */ if (len == 2 && name[0] == '.' && name[1] == '.') { struct smb_inode_info *parent = SMB_INOP(dir)->dir; if (parent->state == SMB_INODE_CACHED) { parent->state = SMB_INODE_LOOKED_UP; } *result = iget(dir->i_sb, smb_info_ino(parent)); iput(dir); if (*result == 0) { return -EACCES; } return 0; } result_info = smb_find_dir_inode(dir, name, len); in_tree: if (result_info != NULL) { if (result_info->state == SMB_INODE_CACHED) { result_info->state = SMB_INODE_LOOKED_UP; } *result = iget(dir->i_sb, smb_info_ino(result_info)); iput(dir); if (new_inode_info != NULL) { smb_kfree_s(new_inode_info, sizeof(struct smb_inode_info)); } if (*result == NULL) { return -EACCES; } return 0; } /* If the file is in the dir cache, we do not have to ask the server. */ found_in_cache = 0; if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino) && (c_size != 0)) { int first = c_last_returned_index; int i; i = first; do { if (compare_filename(SMB_SERVER(dir), name, len, &(c_entry[i])) == 0) { finfo = c_entry[i]; found_in_cache = 1; break; } i = (i + 1) % c_size; } while (i != first); } if (found_in_cache == 0) { DPRINTK("smb_lookup: not found in cache: %s\n", name); if (len > SMB_MAXNAMELEN) { iput(dir); return -ENAMETOOLONG; } error = smb_proc_getattr(dir, name, len, &finfo); if (error < 0) { iput(dir); return error; } finfo.f_ino = smb_fresh_inodes(SMB_SERVER(dir), 1); } new_inode_info = smb_kmalloc(sizeof(struct smb_inode_info), GFP_KERNEL); /* Here somebody else might have inserted the inode */ result_info = smb_find_dir_inode(dir, name, len); if (result_info != NULL) { goto in_tree; } if (new_inode_info == NULL) { iput(dir); return -ENOMEM; } new_inode_info->finfo = finfo; DPRINTK("attr: %x\n", finfo.attr); if ((*result = smb_iget(dir, new_inode_info)) == NULL) { iput(dir); return -EACCES; } DDPRINTK("smb_lookup: %s => %lu\n", name, (unsigned long) result_info); iput(dir); return 0; }
static int smb_readdir(struct inode *dir, struct file *filp, void *dirent, filldir_t filldir) { int result, i = 0; struct smb_dirent *entry = NULL; struct smb_server *server = SMB_SERVER(dir); DPRINTK("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos); DDPRINTK("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n", dir->i_ino, c_ino); if ((dir == NULL) || !S_ISDIR(dir->i_mode)) { printk("smb_readdir: dir is NULL or not a directory\n"); return -EBADF; } if (c_entry == NULL) { i = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE; c_entry = (struct smb_dirent *) smb_vmalloc(i); if (c_entry == NULL) { printk("smb_readdir: no MEMORY for cache\n"); return -ENOMEM; } } if (filp->f_pos == 0) { c_ino = 0; c_dev = 0; c_seen_eof = 0; if (filldir(dirent, ".", 1, filp->f_pos, smb_info_ino(SMB_INOP(dir))) < 0) { return 0; } filp->f_pos += 1; } if (filp->f_pos == 1) { if (filldir(dirent, "..", 2, filp->f_pos, smb_info_ino(SMB_INOP(dir)->dir)) < 0) { return 0; } filp->f_pos += 1; } entry = smb_search_in_cache(dir, filp->f_pos); if (entry == NULL) { if (c_seen_eof) { /* End of directory */ return 0; } result = smb_refill_dir_cache(server, dir, filp->f_pos); if (result <= 0) { return result; } entry = c_entry; } while (entry < &(c_entry[c_size])) { /* We found it. For getwd(), we have to return the correct inode in d_ino if the inode is currently in use. Otherwise the inode number does not matter. (You can argue a lot about this..) */ struct smb_inode_info *ino_info = smb_find_dir_inode(dir, entry->name, entry->len); ino_t ino = entry->f_ino; if (ino_info != NULL) { ino = smb_info_ino(ino_info); } DDPRINTK("smb_readdir: entry->name = %s\n", entry->name); DDPRINTK("smb_readdir: entry->f_pos = %ld\n", entry->f_pos); if (filldir(dirent, entry->name, strlen(entry->name), entry->f_pos, ino) < 0) { break; } if ((dir->i_dev != c_dev) || (dir->i_ino != c_ino) || (entry->f_pos != filp->f_pos)) { /* Someone has destroyed the cache while we slept in filldir */ break; } filp->f_pos += 1; entry += 1; } return 0; }
static void smb_read_inode(struct inode *inode) { /* Our task should be extremely simple here. We only have to look up the information somebody else (smb_iget) put into the inode tree. The address of this information is the inode->i_ino. Just to make sure everything went well, we check it's there. */ struct smb_inode_info *inode_info = (struct smb_inode_info *)(inode->i_ino); #if 1 struct smb_inode_info *root = &(SMB_SERVER(inode)->root); struct smb_inode_info *check_info = root; do { if (inode_info == check_info) { if (check_info->state == SMB_INODE_LOOKED_UP) { DDPRINTK("smb_read_inode: found it!\n"); goto good; } else { printk("smb_read_inode: " "state != SMB_INODE_LOOKED_UP\n"); return; } } check_info = check_info->next; } while (check_info != root); /* Ok, now we're in trouble. The inode info is not there. What should we do now??? */ printk("smb_read_inode: inode info not found\n"); return; good: #endif inode_info->state = SMB_INODE_VALID; SMB_INOP(inode) = inode_info; if (SMB_INOP(inode)->finfo.attr & aDIR) inode->i_mode = SMB_SERVER(inode)->m.dir_mode; else inode->i_mode = SMB_SERVER(inode)->m.file_mode; DDPRINTK("smb_read_inode: inode->i_mode = %u\n", inode->i_mode); inode->i_nlink = 1; inode->i_uid = SMB_SERVER(inode)->m.uid; inode->i_gid = SMB_SERVER(inode)->m.gid; inode->i_size = SMB_INOP(inode)->finfo.size; inode->i_blksize = 1024; inode->i_rdev = 0; if ((inode->i_blksize != 0) && (inode->i_size != 0)) inode->i_blocks = (inode->i_size - 1) / inode->i_blksize + 1; else inode->i_blocks = 0; inode->i_mtime = SMB_INOP(inode)->finfo.mtime; inode->i_ctime = SMB_INOP(inode)->finfo.ctime; inode->i_atime = SMB_INOP(inode)->finfo.atime; if (S_ISREG(inode->i_mode)) inode->i_op = &smb_file_inode_operations; else if (S_ISDIR(inode->i_mode)) inode->i_op = &smb_dir_inode_operations; else inode->i_op = NULL; }
/* DO MORE */ int smb_notify_change(struct inode *inode, struct iattr *attr) { int error = 0; 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 ((attr->ia_valid & ATTR_SIZE) != 0) { if ((error = smb_make_open(inode, O_WRONLY)) < 0) goto fail; if ((error = smb_proc_trunc(SMB_SERVER(inode), SMB_FINFO(inode)->fileid, attr->ia_size)) < 0) goto fail; } if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0) { struct smb_dirent finfo; finfo.attr = 0; if ((attr->ia_valid & ATTR_CTIME) != 0) finfo.ctime = attr->ia_ctime; else finfo.ctime = inode->i_ctime; if ((attr->ia_valid & ATTR_MTIME) != 0) finfo.mtime = attr->ia_mtime; else finfo.mtime = inode->i_mtime; if ((attr->ia_valid & ATTR_ATIME) != 0) finfo.atime = attr->ia_atime; else finfo.atime = inode->i_atime; if ((error = smb_proc_setattr(SMB_SERVER(inode), inode, &finfo)) >= 0) { inode->i_ctime = finfo.ctime; inode->i_mtime = finfo.mtime; inode->i_atime = finfo.atime; } } fail: smb_invalid_dir_cache((unsigned long)(SMB_INOP(inode)->dir)); return error; }
/* * Fill in the supplied page for mmap */ static unsigned long smb_file_mmap_nopage(struct vm_area_struct *area, unsigned long address, int no_share) { struct inode *inode = area->vm_inode; unsigned long page; unsigned int clear; unsigned long tmp; int n; int i; int pos; page = __get_free_page(GFP_KERNEL); if (!page) return 0; address &= PAGE_MASK; pos = address - area->vm_start + area->vm_offset; clear = 0; if (address + PAGE_SIZE > area->vm_end) { clear = address + PAGE_SIZE - area->vm_end; } /* what we can read in one go */ n = SMB_SERVER(inode)->max_xmit - SMB_HEADER_LEN - 5 * 2 - 3 - 10; if (smb_make_open(inode, O_RDONLY) < 0) { clear = PAGE_SIZE; } else { for (i = 0; i < (PAGE_SIZE - clear); i += n) { int hunk, result; hunk = PAGE_SIZE - i; if (hunk > n) hunk = n; DDPRINTK("smb_file_mmap_nopage: reading\n"); DDPRINTK("smb_file_mmap_nopage: pos = %d\n", pos); result = smb_proc_read(SMB_SERVER(inode), SMB_FINFO(inode), pos, hunk, (char *) (page + i), 0); DDPRINTK("smb_file_mmap_nopage: result= %d\n", result); if (result < 0) break; pos += result; if (result < n) { i += result; break; } } } tmp = page + PAGE_SIZE; while (clear--) { *(char *) --tmp = 0; } return page; }