static int smb_dir_open(struct inode *dir, struct file *file) { struct dentry *dentry = file->f_path.dentry; struct smb_sb_info *server; int error = 0; VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name, file->f_path.dentry->d_name.name); /* * Directory timestamps in the core protocol aren't updated * when a file is added, so we give them a very short TTL. */ lock_kernel(); server = server_from_dentry(dentry); if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) { unsigned long age = jiffies - SMB_I(dir)->oldmtime; if (age > 2*HZ) smb_invalid_dir_cache(dir); } /* * Note: in order to allow the smbmount process to open the * mount point, we only revalidate if the connection is valid or * if the process is trying to access something other than the root. */ if (server->state == CONN_VALID || !IS_ROOT(dentry)) error = smb_revalidate_inode(dentry); unlock_kernel(); return error; }
/* * 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; }
/* * Note: in order to allow the smbclient process to open the * mount point, we don't revalidate if conn_pid is NULL. */ static int smb_dir_open(struct inode *dir, struct file *file) { struct dentry *dentry = file->f_dentry; struct smb_sb_info *server = server_from_dentry(dentry); int error = 0; #ifdef SMBFS_DEBUG_VERBOSE printk("smb_dir_open: (%s/%s)\n", dentry->d_parent->d_name.name, file->f_dentry->d_name.name); #endif /* * Directory timestamps in the core protocol aren't updated * when a file is added, so we give them a very short TTL. */ if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) { unsigned long age = jiffies - dir->u.smbfs_i.oldmtime; if (age > 2*HZ) smb_invalid_dir_cache(dir); } if (server->conn_pid) error = smb_revalidate_inode(dentry); return error; }
/* * 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; }
/* * Note: in order to allow the smbclient process to open the * mount point, we don't revalidate if conn_pid is NULL. */ static int smb_dir_open(struct inode *dir, struct file *file) { struct dentry *dentry = file->f_dentry; struct smb_sb_info *server = server_from_dentry(dentry); int error = 0; VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name, file->f_dentry->d_name.name); if (server->conn_pid) error = smb_revalidate_inode(dentry); return error; }
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 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; }
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; }
/* * This is the callback when the dcache has a lookup hit. */ static int smb_lookup_validate(struct 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 5 seconds. (But each * successful server lookup renews the timestamp.) */ valid = (age <= SMBFS_MAX_AGE); #ifdef SMBFS_DEBUG_VERBOSE if (!valid) printk("smb_lookup_validate: %s/%s not valid, age=%lu\n", dentry->d_parent->d_name.name, dentry->d_name.name, age); #endif if (inode) { if (is_bad_inode(inode)) { #ifdef SMBFS_PARANOIA printk("smb_lookup_validate: %s/%s has dud inode\n", dentry->d_parent->d_name.name, dentry->d_name.name); #endif valid = 0; } else if (!valid) valid = (smb_revalidate_inode(dentry) == 0); } else { /* * What should we do for negative dentries? */ } return valid; }
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; }
/* * 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; }
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; int result; #ifdef SMBFS_DEBUG_VERBOSE printk("smb_readdir: reading %s/%s, f_pos=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp->f_pos); #endif /* * 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. */ if (!cachep->valid) { result = smb_refill_dircache(cachep, dentry); if (result) goto out_free; } result = 0; switch ((unsigned int) filp->f_pos) { case 0: if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0) goto out_free; filp->f_pos = 1; case 1: if (filldir(dirent, "..", 2, 1, dentry->d_parent->d_inode->i_ino) < 0) goto out_free; filp->f_pos = 2; } 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_free: smb_free_dircache(cachep); out: return result; }