/* * Write to a file (through the page cache). */ static ssize_t smb_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct dentry * dentry = file->f_dentry; ssize_t result; VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), (unsigned long) count, (unsigned long) *ppos); result = smb_revalidate_inode(dentry); if (result) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), result); goto out; } result = smb_open(dentry, SMB_O_WRONLY); if (result) goto out; if (count > 0) { result = generic_file_write(file, buf, count, ppos); VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", (long) file->f_pos, (long) dentry->d_inode->i_size, dentry->d_inode->i_mtime, dentry->d_inode->i_atime); } out: return result; }
static ssize_t smb_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct file * file = iocb->ki_filp; struct dentry * dentry = file->f_path.dentry; ssize_t status; VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), (unsigned long) iocb->ki_left, (unsigned long) pos); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", (long)dentry->d_inode->i_size, dentry->d_inode->i_flags, dentry->d_inode->i_atime.tv_sec); status = generic_file_aio_read(iocb, iov, nr_segs, pos); out: return status; }
/* * This is the callback when the dcache has a lookup hit. */ static int smb_lookup_validate(struct dentry * dentry, struct nameidata *nd) { struct smb_sb_info *server = server_from_dentry(dentry); struct inode * inode = dentry->d_inode; unsigned long age = jiffies - dentry->d_time; int valid; /* * The default validation is based on dentry age: * we believe in dentries for a few seconds. (But each * successful server lookup renews the timestamp.) */ valid = (age <= SMB_MAX_AGE(server)); #ifdef SMBFS_DEBUG_VERBOSE if (!valid) VERBOSE("%s/%s not valid, age=%lu\n", DENTRY_PATH(dentry), age); #endif if (inode) { lock_kernel(); if (is_bad_inode(inode)) { PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry)); valid = 0; } else if (!valid) valid = (smb_revalidate_inode(dentry) == 0); unlock_kernel(); } else { /* * What should we do for negative dentries? */ } return valid; }
/* * Write to a file (through the page cache). */ static ssize_t smb_file_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct file * file = iocb->ki_filp; struct dentry * dentry = file->f_path.dentry; ssize_t result; VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), (unsigned long) iocb->ki_left, (unsigned long) pos); result = smb_revalidate_inode(dentry); if (result) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), result); goto out; } result = smb_open(dentry, SMB_O_WRONLY); if (result) goto out; if (iocb->ki_left > 0) { result = generic_file_aio_write(iocb, iov, nr_segs, pos); VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n", (long) file->f_pos, (long) dentry->d_inode->i_size, dentry->d_inode->i_mtime, dentry->d_inode->i_atime); } out: return result; }
int smb_follow_link(struct dentry *dentry, struct nameidata *nd) { char *link; int result; DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); result = -ENOMEM; link = kmalloc(SMB_MAXNAMELEN + 1, GFP_KERNEL); if (!link) goto out; result = smb_proc_read_link(server_from_dentry(dentry), dentry, link, SMB_MAXNAMELEN); if (result < 0 || result >= SMB_MAXNAMELEN) goto out_free; link[result] = 0; result = vfs_follow_link(nd, link); out_free: kfree(link); out: return result; }
int smb_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) { DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry)); smb_invalid_dir_cache(dir); return smb_proc_symlink(server_from_dentry(dentry), dentry, oldname); }
/* * Read a page synchronously. */ static int smb_readpage_sync(struct dentry *dentry, struct page *page) { char *buffer = kmap(page); loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT; struct smb_sb_info *server = server_from_dentry(dentry); unsigned int rsize = smb_get_rsize(server); int count = PAGE_SIZE; int result; VERBOSE("file %s/%s, count=%d@%ld, rsize=%d\n", DENTRY_PATH(dentry), count, offset, rsize); result = smb_open(dentry, SMB_O_RDONLY); if (result < 0) { PARANOIA("%s/%s open failed, error=%d\n", DENTRY_PATH(dentry), result); goto io_error; } do { if (count < rsize) rsize = count; result = server->ops->read(dentry->d_inode,offset,rsize,buffer); if (result < 0) goto io_error; count -= result; offset += result; buffer += result; dentry->d_inode->i_atime = CURRENT_TIME; if (result < rsize) break; } while (count); memset(buffer, 0, count); flush_dcache_page(page); SetPageUptodate(page); result = 0; io_error: kunmap(page); UnlockPage(page); return result; }
static int smb_file_mmap(struct file * file, struct vm_area_struct * vma) { struct dentry * dentry = file->f_dentry; int status; VERBOSE("file %s/%s, address %lu - %lu\n", DENTRY_PATH(dentry), vma->vm_start, vma->vm_end); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%d\n", DENTRY_PATH(dentry), status); goto out; } status = generic_file_mmap(file, vma); out: return status; }
static int smb_updatepage(struct file *file, struct page *page, unsigned long offset, unsigned int count) { struct dentry *dentry = file->f_dentry; DEBUG1("(%s/%s %d@%ld)\n", DENTRY_PATH(dentry), count, (page->index << PAGE_CACHE_SHIFT)+offset); return smb_writepage_sync(dentry->d_inode, page, offset, count); }
static ssize_t smb_file_sendfile(struct file *file, loff_t *ppos, size_t count, read_actor_t actor, void *target) { struct dentry *dentry = file->f_path.dentry; ssize_t status; VERBOSE("file %s/%s, pos=%Ld, count=%d\n", DENTRY_PATH(dentry), *ppos, count); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } status = generic_file_sendfile(file, ppos, count, actor, target); out: return status; }
int smb_refill_dircache(struct cache_head * cachep, struct dentry *dentry) { struct inode * inode = dentry->d_inode; int result; VERBOSE("cache %s/%s, blocks=%d\n", DENTRY_PATH(dentry), cachep->pages); /* * Fill the cache, starting at position 2. */ retry: inode->u.smbfs_i.cache_valid |= SMB_F_CACHEVALID; result = smb_proc_readdir(dentry, 2, cachep); if (result < 0) { PARANOIA("readdir failed, result=%d\n", result); goto out; } /* * Check whether the cache was invalidated while * we were doing the scan ... */ if (!(inode->u.smbfs_i.cache_valid & SMB_F_CACHEVALID)) { PARANOIA("cache invalidated, retrying\n"); goto retry; } result = cachep->status; if (!result) { cachep->valid = 1; cachep->mtime = dentry->d_inode->i_mtime; } VERBOSE("cache %s/%s status=%d, entries=%d\n", DENTRY_PATH(dentry), cachep->status, cachep->entries); out: return result; }
static ssize_t smb_file_splice_read(struct file *file, loff_t *ppos, struct pipe_inode_info *pipe, size_t count, unsigned int flags) { struct dentry *dentry = file->f_path.dentry; ssize_t status; VERBOSE("file %s/%s, pos=%Ld, count=%lu\n", DENTRY_PATH(dentry), *ppos, count); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } status = generic_file_splice_read(file, ppos, pipe, count, flags); out: return status; }
/* * Get a pointer to the cache_head structure, * mapped as the page at offset 0. The page is * kept locked while we're using the cache. */ struct cache_head * smb_get_dircache(struct dentry * dentry) { struct inode * inode = dentry->d_inode; struct cache_head * cachep; VERBOSE("finding cache for %s/%s\n", DENTRY_PATH(dentry)); cachep = (struct cache_head *) get_cached_page(inode, 0, 1); if (!cachep) goto out; if (cachep->valid) { struct cache_index * index = cachep->index; struct cache_block * block; unsigned long offset; int i; cachep->valid = 0; /* * Here we only want to find existing cache blocks, * not add new ones. */ for (i = 0; i < cachep->pages; i++, index++) { #ifdef SMBFS_PARANOIA if (index->block) PARANOIA("cache %s/%s has existing block!\n", DENTRY_PATH(dentry)); #endif offset = PAGE_SIZE + (i << PAGE_SHIFT); block = (struct cache_block *) get_cached_page(inode, offset, 0); if (!block) goto out; index->block = block; } cachep->valid = 1; } out: return cachep; }
/* * This is called to update the inode attributes after * we've made changes to a file or directory. */ static int smb_refresh_inode(struct dentry *dentry) { struct inode *inode = dentry->d_inode; int error; struct smb_fattr fattr; error = smb_proc_getattr(dentry, &fattr); if (!error) { smb_renew_times(dentry); /* * Check whether the type part of the mode changed, * and don't update the attributes if it did. * * And don't dick with the root inode */ if (inode->i_ino == 2) return error; if (S_ISLNK(inode->i_mode)) return error; /* VFS will deal with it */ if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) { smb_set_inode_attr(inode, &fattr); } else { /* * Big trouble! The inode has become a new object, * so any operations attempted on it are invalid. * * To limit damage, mark the inode as bad so that * subsequent lookup validations will fail. */ PARANOIA("%s/%s changed mode, %07o to %07o\n", DENTRY_PATH(dentry), inode->i_mode, fattr.f_mode); fattr.f_mode = inode->i_mode; /* save mode */ make_bad_inode(inode); inode->i_mode = fattr.f_mode; /* restore mode */ /* * No need to worry about unhashing the dentry: the * lookup validation will see that the inode is bad. * But we do want to invalidate the caches ... */ if (!S_ISDIR(inode->i_mode)) invalidate_remote_inode(inode); else smb_invalid_dir_cache(inode); error = -EIO; } } return error; }
static ssize_t smb_file_read(struct file * file, char * buf, size_t count, loff_t *ppos) { struct dentry * dentry = file->f_dentry; ssize_t status; VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry), (unsigned long) count, (unsigned long) *ppos); status = smb_revalidate_inode(dentry); if (status) { PARANOIA("%s/%s validation failed, error=%Zd\n", DENTRY_PATH(dentry), status); goto out; } VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n", (long)dentry->d_inode->i_size, dentry->d_inode->i_flags, dentry->d_inode->i_atime); status = generic_file_read(file, buf, count, ppos); out: return status; }
static int smb_fsync(struct file *file, struct dentry * dentry, int datasync) { struct smb_sb_info *server = server_from_dentry(dentry); int result; VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry)); /* * The VFS will writepage() all dirty pages for us, but we * should send a SMBflush to the server, letting it know that * we want things synchronized with actual storage. * * Note: this function requires all pages to have been written already * (should be ok with writepage_sync) */ result = smb_proc_flush(server, SMB_I(dentry->d_inode)->fileid); return result; }
static int smb_follow_link(struct dentry *dentry, struct nameidata *nd) { char *link = __getname(); DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry)); if (!link) { link = ERR_PTR(-ENOMEM); } else { int len = smb_proc_read_link(server_from_dentry(dentry), dentry, link, PATH_MAX - 1); if (len < 0) { __putname(link); link = ERR_PTR(len); } else { link[len] = 0; } } nd_set_link(nd, link); return 0; }
/* * Read a directory, using filldir to fill the dirent memory. * smb_proc_readdir does the actual reading from the smb server. * * The cache code is almost directly taken from ncpfs */ static int smb_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_path.dentry; struct inode *dir = dentry->d_inode; struct smb_sb_info *server = server_from_dentry(dentry); union smb_dir_cache *cache = NULL; struct smb_cache_control ctl; struct page *page = NULL; int result; ctl.page = NULL; ctl.cache = NULL; VERBOSE("reading %s/%s, f_pos=%d\n", DENTRY_PATH(dentry), (int) filp->f_pos); result = 0; lock_kernel(); switch ((unsigned int) filp->f_pos) { case 0: if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0) goto out; filp->f_pos = 1; /* fallthrough */ case 1: if (filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR) < 0) goto out; filp->f_pos = 2; } /* * Make sure our inode is up-to-date. */ result = smb_revalidate_inode(dentry); if (result) goto out; page = grab_cache_page(&dir->i_data, 0); if (!page) goto read_really; ctl.cache = cache = kmap(page); ctl.head = cache->head; if (!PageUptodate(page) || !ctl.head.eof) { VERBOSE("%s/%s, page uptodate=%d, eof=%d\n", DENTRY_PATH(dentry), PageUptodate(page),ctl.head.eof); goto init_cache; } if (filp->f_pos == 2) { if (jiffies - ctl.head.time >= SMB_MAX_AGE(server)) goto init_cache; /* * N.B. ncpfs checks mtime of dentry too here, we don't. * 1. common smb servers do not update mtime on dir changes * 2. it requires an extra smb request * (revalidate has the same timeout as ctl.head.time) * * Instead smbfs invalidates its own cache on local changes * and remote changes are not seen until timeout. */ } if (filp->f_pos > ctl.head.end) goto finished; ctl.fpos = filp->f_pos + (SMB_DIRCACHE_START - 2); ctl.ofs = ctl.fpos / SMB_DIRCACHE_SIZE; ctl.idx = ctl.fpos % SMB_DIRCACHE_SIZE; for (;;) { if (ctl.ofs != 0) { ctl.page = find_lock_page(&dir->i_data, ctl.ofs); if (!ctl.page) goto invalid_cache; ctl.cache = kmap(ctl.page); if (!PageUptodate(ctl.page)) goto invalid_cache; } while (ctl.idx < SMB_DIRCACHE_SIZE) { struct dentry *dent; int res; dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx], dentry, filp->f_pos); if (!dent) goto invalid_cache; res = filldir(dirent, dent->d_name.name, dent->d_name.len, filp->f_pos, dent->d_inode->i_ino, DT_UNKNOWN); dput(dent); if (res) goto finished; filp->f_pos += 1; ctl.idx += 1; if (filp->f_pos > ctl.head.end) goto finished; } if (ctl.page) { kunmap(ctl.page); SetPageUptodate(ctl.page); unlock_page(ctl.page); page_cache_release(ctl.page); ctl.page = NULL; } ctl.idx = 0; ctl.ofs += 1; } invalid_cache: if (ctl.page) { kunmap(ctl.page); unlock_page(ctl.page); page_cache_release(ctl.page); ctl.page = NULL; } ctl.cache = cache; init_cache: smb_invalidate_dircache_entries(dentry); ctl.head.time = jiffies; ctl.head.eof = 0; ctl.fpos = 2; ctl.ofs = 0; ctl.idx = SMB_DIRCACHE_START; ctl.filled = 0; ctl.valid = 1; read_really: result = server->ops->readdir(filp, dirent, filldir, &ctl); if (result == -ERESTARTSYS && page) ClearPageUptodate(page); if (ctl.idx == -1) goto invalid_cache; /* retry */ ctl.head.end = ctl.fpos - 1; ctl.head.eof = ctl.valid; finished: if (page) { cache->head = ctl.head; kunmap(page); if (result != -ERESTARTSYS) SetPageUptodate(page); unlock_page(page); page_cache_release(page); } if (ctl.page) { kunmap(ctl.page); SetPageUptodate(ctl.page); unlock_page(ctl.page); page_cache_release(ctl.page); } out: unlock_kernel(); return result; }
static int smb_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct dentry *dentry = filp->f_dentry; struct inode *dir = dentry->d_inode; struct cache_head *cachep = NULL; int result; VERBOSE("reading %s/%s, f_pos=%d\n", DENTRY_PATH(dentry), (int) filp->f_pos); result = 0; switch ((unsigned int) filp->f_pos) { case 0: if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0) goto out; filp->f_pos = 1; case 1: if (filldir(dirent, "..", 2, 1, dentry->d_parent->d_inode->i_ino) < 0) goto out; filp->f_pos = 2; } /* * Make sure our inode is up-to-date. */ result = smb_revalidate_inode(dentry); if (result) goto out; /* * Get the cache pointer ... */ result = -EIO; cachep = smb_get_dircache(dentry); if (!cachep) goto out; /* * Make sure the cache is up-to-date. * * To detect changes on the server we refill on each "new" access. * * Directory mtime would be nice to use for finding changes, * unfortunately some servers (NT4) doesn't update on local changes. */ if (!cachep->valid || filp->f_pos == 2) { result = smb_refill_dircache(cachep, dentry); if (result) goto out; } result = 0; while (1) { struct cache_dirent this_dirent, *entry = &this_dirent; if (!smb_find_in_cache(cachep, filp->f_pos, entry)) break; /* * Check whether to look up the inode number. */ if (!entry->ino) { struct qstr qname; /* N.B. Make cache_dirent name a qstr! */ qname.name = entry->name; qname.len = entry->len; entry->ino = find_inode_number(dentry, &qname); if (!entry->ino) entry->ino = smb_invent_inos(1); } if (filldir(dirent, entry->name, entry->len, filp->f_pos, entry->ino) < 0) break; filp->f_pos += 1; } /* * Release the dircache. */ out: if (cachep) { smb_free_dircache(cachep); } return result; }