/* * map the AFS file status to the inode member variables */ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) { struct inode *inode = AFS_VNODE_TO_I(vnode); _debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu", vnode->status.type, vnode->status.nlink, (unsigned long long) vnode->status.size, vnode->status.data_version, vnode->status.mode); switch (vnode->status.type) { case AFS_FTYPE_FILE: inode->i_mode = S_IFREG | vnode->status.mode; inode->i_op = &afs_file_inode_operations; inode->i_fop = &afs_file_operations; break; case AFS_FTYPE_DIR: inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_op = &afs_dir_inode_operations; inode->i_fop = &afs_dir_file_operations; break; case AFS_FTYPE_SYMLINK: inode->i_mode = S_IFLNK | vnode->status.mode; inode->i_op = &page_symlink_inode_operations; break; default: printk("kAFS: AFS vnode with undefined type\n"); return -EBADMSG; } #ifdef CONFIG_AFS_FSCACHE if (vnode->status.size != inode->i_size) fscache_attr_changed(vnode->cache); #endif set_nlink(inode, vnode->status.nlink); inode->i_uid = vnode->status.owner; inode->i_gid = 0; inode->i_size = vnode->status.size; inode->i_ctime.tv_sec = vnode->status.mtime_server; inode->i_ctime.tv_nsec = 0; inode->i_atime = inode->i_mtime = inode->i_ctime; inode->i_blocks = 0; inode->i_version = vnode->fid.unique; inode->i_mapping->a_ops = &afs_fs_aops; /* check to see whether a symbolic link is really a mountpoint */ if (vnode->status.type == AFS_FTYPE_SYMLINK) { afs_mntpt_check_symlink(vnode, key); if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) { inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_op = &afs_mntpt_inode_operations; inode->i_fop = &afs_mntpt_file_operations; } } return 0; }
/* * map the AFS file status to the inode member variables */ static int afs_inode_map_status(struct afs_vnode *vnode) { struct inode *inode = AFS_VNODE_TO_I(vnode); _debug("FS: ft=%d lk=%d sz=%Zu ver=%Lu mod=%hu", vnode->status.type, vnode->status.nlink, vnode->status.size, vnode->status.version, vnode->status.mode); switch (vnode->status.type) { case AFS_FTYPE_FILE: inode->i_mode = S_IFREG | vnode->status.mode; inode->i_op = &afs_file_inode_operations; inode->i_fop = &afs_file_file_operations; break; case AFS_FTYPE_DIR: inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_op = &afs_dir_inode_operations; inode->i_fop = &afs_dir_file_operations; break; case AFS_FTYPE_SYMLINK: inode->i_mode = S_IFLNK | vnode->status.mode; inode->i_op = &page_symlink_inode_operations; break; default: printk("kAFS: AFS vnode with undefined type\n"); return -EBADMSG; } inode->i_nlink = vnode->status.nlink; inode->i_uid = vnode->status.owner; inode->i_gid = 0; inode->i_size = vnode->status.size; inode->i_ctime.tv_sec = vnode->status.mtime_server; inode->i_ctime.tv_nsec = 0; inode->i_atime = inode->i_mtime = inode->i_ctime; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_version = vnode->fid.unique; inode->i_mapping->a_ops = &afs_fs_aops; /* check to see whether a symbolic link is really a mountpoint */ if (vnode->status.type == AFS_FTYPE_SYMLINK) { afs_mntpt_check_symlink(vnode); if (vnode->flags & AFS_VNODE_MOUNTPOINT) { inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_op = &afs_mntpt_inode_operations; inode->i_fop = &afs_mntpt_file_operations; } } return 0; } /* end afs_inode_map_status() */
/* * check a symbolic link to see whether it actually encodes a mountpoint * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately */ int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key) { struct file file = { .private_data = key, }; struct page *page; size_t size; char *buf; int ret; _enter("{%x:%u,%u}", vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique); /* read the contents of the symlink into the pagecache */ page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, &file); if (IS_ERR(page)) { ret = PTR_ERR(page); goto out; } ret = -EIO; if (PageError(page)) goto out_free; buf = kmap(page); /* examine the symlink's contents */ size = vnode->status.size; _debug("symlink to %*.*s", (int) size, (int) size, buf); if (size > 2 && (buf[0] == '%' || buf[0] == '#') && buf[size - 1] == '.' ) { _debug("symlink is a mountpoint"); spin_lock(&vnode->lock); set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); spin_unlock(&vnode->lock); } ret = 0; kunmap(page); out_free: page_cache_release(page); out: _leave(" = %d", ret); return ret; }
/* * check a symbolic link to see whether it actually encodes a mountpoint * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately */ int afs_mntpt_check_symlink(struct afs_vnode *vnode) { struct page *page; size_t size; char *buf; int ret; _enter("{%u,%u}", vnode->fid.vnode, vnode->fid.unique); /* read the contents of the symlink into the pagecache */ page = read_mapping_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0, NULL); if (IS_ERR(page)) { ret = PTR_ERR(page); goto out; } ret = -EIO; wait_on_page_locked(page); buf = kmap(page); if (!PageUptodate(page)) goto out_free; if (PageError(page)) goto out_free; /* examine the symlink's contents */ size = vnode->status.size; _debug("symlink to %*.*s", size, (int) size, buf); if (size > 2 && (buf[0] == '%' || buf[0] == '#') && buf[size - 1] == '.' ) { _debug("symlink is a mountpoint"); spin_lock(&vnode->lock); vnode->flags |= AFS_VNODE_MOUNTPOINT; spin_unlock(&vnode->lock); } ret = 0; out_free: kunmap(page); page_cache_release(page); out: _leave(" = %d", ret); return ret; } /* end afs_mntpt_check_symlink() */
/* * allow the fileserver to explicitly break one callback * - happens when * - the backing file is changed * - a lock is released */ static void afs_break_one_callback(struct afs_server *server, struct afs_fid *fid) { struct afs_vnode *vnode; struct rb_node *p; _debug("find"); spin_lock(&server->fs_lock); p = server->fs_vnodes.rb_node; while (p) { vnode = rb_entry(p, struct afs_vnode, server_rb); if (fid->vid < vnode->fid.vid) p = p->rb_left; else if (fid->vid > vnode->fid.vid) p = p->rb_right; else if (fid->vnode < vnode->fid.vnode) p = p->rb_left; else if (fid->vnode > vnode->fid.vnode) p = p->rb_right; else if (fid->unique < vnode->fid.unique) p = p->rb_left; else if (fid->unique > vnode->fid.unique) p = p->rb_right; else goto found; } /* not found so we just ignore it (it may have moved to another * server) */ not_available: _debug("not avail"); spin_unlock(&server->fs_lock); _leave(""); return; found: _debug("found"); ASSERTCMP(server, ==, vnode->server); if (!igrab(AFS_VNODE_TO_I(vnode))) goto not_available; spin_unlock(&server->fs_lock); afs_break_callback(server, vnode); iput(&vnode->vfs_inode); _leave(""); }
/* * map the AFS file status to the inode member variables */ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) { struct inode *inode = AFS_VNODE_TO_I(vnode); bool changed; _debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu", vnode->status.type, vnode->status.nlink, (unsigned long long) vnode->status.size, vnode->status.data_version, vnode->status.mode); read_seqlock_excl(&vnode->cb_lock); switch (vnode->status.type) { case AFS_FTYPE_FILE: inode->i_mode = S_IFREG | vnode->status.mode; inode->i_op = &afs_file_inode_operations; inode->i_fop = &afs_file_operations; break; case AFS_FTYPE_DIR: inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_op = &afs_dir_inode_operations; inode->i_fop = &afs_dir_file_operations; break; case AFS_FTYPE_SYMLINK: /* Symlinks with a mode of 0644 are actually mountpoints. */ if ((vnode->status.mode & 0777) == 0644) { inode->i_flags |= S_AUTOMOUNT; set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); inode->i_mode = S_IFDIR | 0555; inode->i_op = &afs_mntpt_inode_operations; inode->i_fop = &afs_mntpt_file_operations; } else { inode->i_mode = S_IFLNK | vnode->status.mode; inode->i_op = &afs_symlink_inode_operations; } inode_nohighmem(inode); break; default: printk("kAFS: AFS vnode with undefined type\n"); read_sequnlock_excl(&vnode->cb_lock); return -EBADMSG; } changed = (vnode->status.size != inode->i_size); set_nlink(inode, vnode->status.nlink); inode->i_uid = vnode->status.owner; inode->i_gid = vnode->status.group; inode->i_size = vnode->status.size; inode->i_ctime.tv_sec = vnode->status.mtime_client; inode->i_ctime.tv_nsec = 0; inode->i_atime = inode->i_mtime = inode->i_ctime; inode->i_blocks = 0; inode->i_generation = vnode->fid.unique; inode_set_iversion_raw(inode, vnode->status.data_version); inode->i_mapping->a_ops = &afs_fs_aops; read_sequnlock_excl(&vnode->cb_lock); #ifdef CONFIG_AFS_FSCACHE if (changed) fscache_attr_changed(vnode->cache); #endif return 0; }