/* * Write a page synchronously. * Offset is the data offset within the page. */ static int smb_writepage_sync(struct inode *inode, struct page *page, unsigned long pageoffset, unsigned int count) { loff_t offset; char *buffer = kmap(page) + pageoffset; struct smb_sb_info *server = server_from_inode(inode); unsigned int wsize = smb_get_wsize(server); int ret = 0; offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset; VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n", inode->i_ino, SMB_I(inode)->fileid, count, offset, wsize); do { int write_ret; if (count < wsize) wsize = count; write_ret = server->ops->write(inode, offset, wsize, buffer); if (write_ret < 0) { PARANOIA("failed write, wsize=%d, write_ret=%d\n", wsize, write_ret); ret = write_ret; break; } /* N.B. what if result < wsize?? */ #ifdef SMBFS_PARANOIA if (write_ret < wsize) PARANOIA("short write, wsize=%d, write_ret=%d\n", wsize, write_ret); #endif buffer += wsize; offset += wsize; count -= wsize; /* * Update the inode now rather than waiting for a refresh. */ inode->i_mtime = inode->i_atime = current_fs_time(inode->i_sb); SMB_I(inode)->flags |= SMB_F_LOCALWRITE; if (offset > inode->i_size) inode->i_size = offset; } while (count); kunmap(page); return ret; }
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; }
/* * Copy the inode data to a smb_fattr structure. */ void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr) { memset(fattr, 0, sizeof(struct smb_fattr)); fattr->f_mode = inode->i_mode; fattr->f_nlink = inode->i_nlink; fattr->f_ino = inode->i_ino; fattr->f_uid = inode->i_uid; fattr->f_gid = inode->i_gid; fattr->f_size = inode->i_size; fattr->f_mtime = inode->i_mtime; fattr->f_ctime = inode->i_ctime; fattr->f_atime = inode->i_atime; fattr->f_blksize= inode->i_blksize; fattr->f_blocks = inode->i_blocks; fattr->attr = SMB_I(inode)->attr; /* * Keep the attributes in sync with the inode permissions. */ if (fattr->f_mode & S_IWUSR) fattr->attr &= ~aRONLY; else fattr->attr |= aRONLY; }
static int smb_file_release(struct inode *inode, struct file * file) { lock_kernel(); if (!--SMB_I(inode)->openers) { /* We must flush any dirty pages now as we won't be able to write anything after close. mmap can trigger this. "openers" should perhaps include mmap'ers ... */ filemap_write_and_wait(inode->i_mapping); smb_close(inode); } unlock_kernel(); return 0; }
/* * This is called when we want to check whether the inode * has changed on the server. If it has changed, we must * invalidate our local caches. */ int smb_revalidate_inode(struct dentry *dentry) { struct smb_sb_info *s = server_from_dentry(dentry); struct inode *inode = dentry->d_inode; int error = 0; DEBUG1("smb_revalidate_inode\n"); lock_kernel(); /* * Check whether we've recently refreshed the inode. */ if (time_before(jiffies, SMB_I(inode)->oldmtime + SMB_MAX_AGE(s))) { VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n", inode->i_ino, jiffies, SMB_I(inode)->oldmtime); goto out; } error = smb_refresh_inode(dentry); out: unlock_kernel(); return error; }
static int smb_file_open(struct inode *inode, struct file * file) { int result; struct dentry *dentry = file->f_path.dentry; int smb_mode = (file->f_mode & O_ACCMODE) - 1; lock_kernel(); result = smb_open(dentry, smb_mode); if (result) goto out; SMB_I(inode)->openers++; out: unlock_kernel(); return result; }
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; }
/* * Update the inode, possibly causing it to invalidate its pages if mtime/size * is different from last time. */ void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) { struct smb_inode_info *ei = SMB_I(inode); /* * A size change should have a different mtime, or same mtime * but different size. */ time_t last_time = inode->i_mtime.tv_sec; loff_t last_sz = inode->i_size; inode->i_mode = fattr->f_mode; inode->i_nlink = fattr->f_nlink; inode->i_uid = fattr->f_uid; inode->i_gid = fattr->f_gid; inode->i_ctime = fattr->f_ctime; inode->i_blksize= fattr->f_blksize; inode->i_blocks = fattr->f_blocks; inode->i_size = fattr->f_size; inode->i_mtime = fattr->f_mtime; inode->i_atime = fattr->f_atime; ei->attr = fattr->attr; /* * Update the "last time refreshed" field for revalidation. */ ei->oldmtime = jiffies; if (inode->i_mtime.tv_sec != last_time || inode->i_size != last_sz) { VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n", inode->i_ino, (long) last_time, (long) inode->i_mtime, (long) last_sz, (long) inode->i_size); if (!S_ISDIR(inode->i_mode)) invalidate_remote_inode(inode); } }
/* We are always generating a new inode here */ struct inode * smb_iget(struct super_block *sb, struct smb_fattr *fattr) { struct smb_sb_info *server = SMB_SB(sb); struct inode *result; DEBUG1("smb_iget: %p\n", fattr); result = new_inode(sb); if (!result) return result; result->i_ino = fattr->f_ino; SMB_I(result)->open = 0; SMB_I(result)->fileid = 0; SMB_I(result)->access = 0; SMB_I(result)->flags = 0; SMB_I(result)->closed = 0; SMB_I(result)->openers = 0; smb_set_inode_attr(result, fattr); if (S_ISREG(result->i_mode)) { result->i_op = &smb_file_inode_operations; result->i_fop = &smb_file_operations; result->i_data.a_ops = &smb_file_aops; } else if (S_ISDIR(result->i_mode)) { if (server->opt.capabilities & SMB_CAP_UNIX) result->i_op = &smb_dir_inode_operations_unix; else result->i_op = &smb_dir_inode_operations; result->i_fop = &smb_dir_operations; } else if (S_ISLNK(result->i_mode)) { result->i_op = &smb_link_inode_operations; } else { init_special_inode(result, result->i_mode, fattr->f_rdev); } insert_inode_hash(result); return result; }
static void smb_destroy_inode(struct inode *inode) { kmem_cache_free(smb_inode_cachep, SMB_I(inode)); }