| @@ -1160,38 +1160,78 @@ static int fuse_permission(struct inode *inode, int mask) · 1159 unmodified lines |
| 1160 | 1160 | return err; |
| 1161 | 1161 | } |
| 1162 | 1162 | |
| 1163 | | −static const char *fuse_get_link(struct dentry *dentry, |
| 1164 | | − struct inode *inode, |
| 1165 | | − struct delayed_call *done) |
| 1163 | +static int fuse_readlink_page(struct inode *inode, struct page *page) |
| 1166 | 1164 | { |
| 1167 | 1165 | struct fuse_conn *fc = get_fuse_conn(inode); |
| 1168 | | − FUSE_ARGS(args); |
| 1169 | | − char *link; |
| 1170 | | − ssize_t ret; |
| 1166 | + struct fuse_req *req; |
| 1167 | + int err; |
| 1171 | 1168 | |
| 1172 | | − if (!dentry) |
| 1173 | | − return ERR_PTR(-ECHILD); |
| 1169 | + req = fuse_get_req(fc, 1); |
| 1170 | + if (IS_ERR(req)) |
| 1171 | + return PTR_ERR(req); |
| 1172 | + |
| 1173 | + req->out.page_zeroing = 1; |
| 1174 | + req->out.argpages = 1; |
| 1175 | + req->num_pages = 1; |
| 1176 | + req->pages[0] = page; |
| 1177 | + req->page_descs[0].length = PAGE_SIZE - 1; |
| 1178 | + req->in.h.opcode = FUSE_READLINK; |
| 1179 | + req->in.h.nodeid = get_node_id(inode); |
| 1180 | + req->out.argvar = 1; |
| 1181 | + req->out.numargs = 1; |
| 1182 | + req->out.args[0].size = PAGE_SIZE - 1; |
| 1183 | + fuse_request_send(fc, req); |
| 1184 | + err = req->out.h.error; |
| 1174 | 1185 | |
| 1175 | | − link = kmalloc(PAGE_SIZE, GFP_KERNEL); |
| 1176 | | − if (!link) |
| 1177 | | − return ERR_PTR(-ENOMEM); |
| 1186 | + if (!err) { |
| 1187 | + char *link = page_address(page); |
| 1188 | + size_t len = req->out.args[0].size; |
| 1178 | 1189 | |
| 1179 | | − args.in.h.opcode = FUSE_READLINK; |
| 1180 | | − args.in.h.nodeid = get_node_id(inode); |
| 1181 | | − args.out.argvar = 1; |
| 1182 | | − args.out.numargs = 1; |
| 1183 | | − args.out.args[0].size = PAGE_SIZE - 1; |
| 1184 | | − args.out.args[0].value = link; |
| 1185 | | − ret = fuse_simple_request(fc, &args); |
| 1186 | | − if (ret < 0) { |
| 1187 | | − kfree(link); |
| 1188 | | − link = ERR_PTR(ret); |
| 1189 | | − } else { |
| 1190 | | − link[ret] = '\0'; |
| 1191 | | − set_delayed_call(done, kfree_link, link); |
| 1190 | + BUG_ON(len >= PAGE_SIZE); |
| 1191 | + link[len] = '\0'; |
| 1192 | 1192 | } |
| 1193 | + |
| 1194 | + fuse_put_request(fc, req); |
| 1193 | 1195 | fuse_invalidate_atime(inode); |
| 1194 | | − return link; |
| 1196 | + |
| 1197 | + return err; |
| 1198 | +} |
| 1199 | + |
| 1200 | +static const char *fuse_get_link(struct dentry *dentry, struct inode *inode, |
| 1201 | + struct delayed_call *callback) |
| 1202 | +{ |
| 1203 | + struct fuse_conn *fc = get_fuse_conn(inode); |
| 1204 | + struct page *page; |
| 1205 | + int err; |
| 1206 | + |
| 1207 | + err = -EIO; |
| 1208 | + if (is_bad_inode(inode)) |
| 1209 | + goto out_err; |
| 1210 | + |
| 1211 | + if (fc->cache_symlinks) |
| 1212 | + return page_get_link(dentry, inode, callback); |
| 1213 | + |
| 1214 | + err = -ECHILD; |
| 1215 | + if (!dentry) |
| 1216 | + goto out_err; |
| 1217 | + |
| 1218 | + page = alloc_page(GFP_KERNEL); |
| 1219 | + err = -ENOMEM; |
| 1220 | + if (!page) |
| 1221 | + goto out_err; |
| 1222 | + |
| 1223 | + err = fuse_readlink_page(inode, page); |
| 1224 | + if (err) { |
| 1225 | + __free_page(page); |
| 1226 | + goto out_err; |
| 1227 | + } |
| 1228 | + |
| 1229 | + set_delayed_call(callback, page_put_link, page); |
| 1230 | + |
| 1231 | + return page_address(page); |
| 1232 | + |
| 1233 | +out_err: |
| 1234 | + return ERR_PTR(err); |
| 1195 | 1235 | } |
| 1196 | 1236 | |
| 1197 | 1237 | static int fuse_dir_open(struct inode *inode, struct file *file) |
| @@ -1644,7 +1684,25 @@ void fuse_init_dir(struct inode *inode) · 446 unmodified lines |
| 1644 | 1684 | fi->rdc.version = 0; |
| 1645 | 1685 | } |
| 1646 | 1686 | |
| 1687 | +static int fuse_symlink_readpage(struct file *null, struct page *page) |
| 1688 | +{ |
| 1689 | + int err = fuse_readlink_page(page->mapping->host, page); |
| 1690 | + |
| 1691 | + if (!err) |
| 1692 | + SetPageUptodate(page); |
| 1693 | + |
| 1694 | + unlock_page(page); |
| 1695 | + |
| 1696 | + return err; |
| 1697 | +} |
| 1698 | + |
| 1699 | +static const struct address_space_operations fuse_symlink_aops = { |
| 1700 | + .readpage = fuse_symlink_readpage, |
| 1701 | +}; |
| 1702 | + |
| 1647 | 1703 | void fuse_init_symlink(struct inode *inode) |
| 1648 | 1704 | { |
| 1649 | 1705 | inode->i_op = &fuse_symlink_inode_operations; |
| 1706 | + inode->i_data.a_ops = &fuse_symlink_aops; |
| 1707 | + inode_nohighmem(inode); |
| 1650 | 1708 | } |