hostfs_kern.c 20.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
Jeff Dike's avatar
Jeff Dike committed
2
 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds's avatar
Linus Torvalds committed
3 4 5 6 7 8 9
 * Licensed under the GPL
 *
 * Ported the filesystem routines to 2.5.
 * 2003-02-10 Petr Baudis <pasky@ucw.cz>
 */

#include <linux/fs.h>
10
#include <linux/magic.h>
Linus Torvalds's avatar
Linus Torvalds committed
11
#include <linux/module.h>
Jeff Dike's avatar
Jeff Dike committed
12
#include <linux/mm.h>
Linus Torvalds's avatar
Linus Torvalds committed
13 14
#include <linux/pagemap.h>
#include <linux/statfs.h>
15
#include <linux/slab.h>
16
#include <linux/seq_file.h>
Jiri Kosina's avatar
Jiri Kosina committed
17
#include <linux/mount.h>
Al Viro's avatar
Al Viro committed
18
#include <linux/namei.h>
Linus Torvalds's avatar
Linus Torvalds committed
19
#include "hostfs.h"
20 21
#include <init.h>
#include <kern.h>
Linus Torvalds's avatar
Linus Torvalds committed
22 23 24

struct hostfs_inode_info {
	int fd;
25
	fmode_t mode;
Linus Torvalds's avatar
Linus Torvalds committed
26 27 28 29 30
	struct inode vfs_inode;
};

static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
{
Jeff Dike's avatar
Jeff Dike committed
31
	return list_entry(inode, struct hostfs_inode_info, vfs_inode);
Linus Torvalds's avatar
Linus Torvalds committed
32 33
}

Al Viro's avatar
Al Viro committed
34
#define FILE_HOSTFS_I(file) HOSTFS_I(file_inode(file))
Linus Torvalds's avatar
Linus Torvalds committed
35 36

/* Changed in hostfs_args before the kernel starts running */
37
static char *root_ino = "";
Linus Torvalds's avatar
Linus Torvalds committed
38 39
static int append = 0;

40 41
static const struct inode_operations hostfs_iops;
static const struct inode_operations hostfs_dir_iops;
Al Viro's avatar
Al Viro committed
42
static const struct inode_operations hostfs_link_iops;
Linus Torvalds's avatar
Linus Torvalds committed
43 44 45 46 47 48 49

#ifndef MODULE
static int __init hostfs_args(char *options, int *add)
{
	char *ptr;

	ptr = strchr(options, ',');
Jeff Dike's avatar
Jeff Dike committed
50
	if (ptr != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
51
		*ptr++ = '\0';
Jeff Dike's avatar
Jeff Dike committed
52
	if (*options != '\0')
Linus Torvalds's avatar
Linus Torvalds committed
53 54 55
		root_ino = options;

	options = ptr;
Jeff Dike's avatar
Jeff Dike committed
56
	while (options) {
Linus Torvalds's avatar
Linus Torvalds committed
57
		ptr = strchr(options, ',');
Jeff Dike's avatar
Jeff Dike committed
58
		if (ptr != NULL)
Linus Torvalds's avatar
Linus Torvalds committed
59
			*ptr++ = '\0';
Jeff Dike's avatar
Jeff Dike committed
60 61
		if (*options != '\0') {
			if (!strcmp(options, "append"))
Linus Torvalds's avatar
Linus Torvalds committed
62 63 64 65 66 67
				append = 1;
			else printf("hostfs_args - unsupported option - %s\n",
				    options);
		}
		options = ptr;
	}
Jeff Dike's avatar
Jeff Dike committed
68
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82
}

__uml_setup("hostfs=", hostfs_args,
"hostfs=<root dir>,<flags>,...\n"
"    This is used to set hostfs parameters.  The root directory argument\n"
"    is used to confine all hostfs mounts to within the specified directory\n"
"    tree on the host.  If this isn't specified, then a user inside UML can\n"
"    mount anything on the host that's accessible to the user that's running\n"
"    it.\n"
"    The only flag currently supported is 'append', which specifies that all\n"
"    files opened by hostfs will be opened in append mode.\n\n"
);
#endif

83
static char *__dentry_name(struct dentry *dentry, char *name)
Linus Torvalds's avatar
Linus Torvalds committed
84
{
Nick Piggin's avatar
Nick Piggin committed
85
	char *p = dentry_path_raw(dentry, name, PATH_MAX);
86 87
	char *root;
	size_t len;
Linus Torvalds's avatar
Linus Torvalds committed
88

89 90 91 92
	root = dentry->d_sb->s_fs_info;
	len = strlen(root);
	if (IS_ERR(p)) {
		__putname(name);
Jeff Dike's avatar
Jeff Dike committed
93
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
94
	}
95
	strlcpy(name, root, PATH_MAX);
96 97 98 99 100 101 102 103 104
	if (len > p - name) {
		__putname(name);
		return NULL;
	}
	if (p > name + len) {
		char *s = name + len;
		while ((*s++ = *p++) != '\0')
			;
	}
Jeff Dike's avatar
Jeff Dike committed
105
	return name;
Linus Torvalds's avatar
Linus Torvalds committed
106 107
}

108 109 110 111 112 113
static char *dentry_name(struct dentry *dentry)
{
	char *name = __getname();
	if (!name)
		return NULL;

114
	return __dentry_name(dentry, name);
115 116
}

117
static char *inode_name(struct inode *ino)
Linus Torvalds's avatar
Linus Torvalds committed
118 119
{
	struct dentry *dentry;
Nick Piggin's avatar
Nick Piggin committed
120
	char *name;
Linus Torvalds's avatar
Linus Torvalds committed
121

Nick Piggin's avatar
Nick Piggin committed
122 123
	dentry = d_find_alias(ino);
	if (!dentry)
124
		return NULL;
Nick Piggin's avatar
Nick Piggin committed
125 126 127 128 129 130

	name = dentry_name(dentry);

	dput(dentry);

	return name;
Linus Torvalds's avatar
Linus Torvalds committed
131 132 133 134 135 136 137 138
}

static char *follow_link(char *link)
{
	int len, n;
	char *name, *resolved, *end;

	len = 64;
Jeff Dike's avatar
Jeff Dike committed
139
	while (1) {
Linus Torvalds's avatar
Linus Torvalds committed
140 141
		n = -ENOMEM;
		name = kmalloc(len, GFP_KERNEL);
Jeff Dike's avatar
Jeff Dike committed
142
		if (name == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
143 144
			goto out;

145
		n = hostfs_do_readlink(link, name, len);
Jeff Dike's avatar
Jeff Dike committed
146
		if (n < len)
Linus Torvalds's avatar
Linus Torvalds committed
147 148 149 150
			break;
		len *= 2;
		kfree(name);
	}
Jeff Dike's avatar
Jeff Dike committed
151
	if (n < 0)
Linus Torvalds's avatar
Linus Torvalds committed
152 153
		goto out_free;

Jeff Dike's avatar
Jeff Dike committed
154
	if (*name == '/')
Jeff Dike's avatar
Jeff Dike committed
155
		return name;
Linus Torvalds's avatar
Linus Torvalds committed
156 157

	end = strrchr(link, '/');
Jeff Dike's avatar
Jeff Dike committed
158
	if (end == NULL)
Jeff Dike's avatar
Jeff Dike committed
159
		return name;
Linus Torvalds's avatar
Linus Torvalds committed
160 161 162 163 164

	*(end + 1) = '\0';
	len = strlen(link) + strlen(name) + 1;

	resolved = kmalloc(len, GFP_KERNEL);
Jeff Dike's avatar
Jeff Dike committed
165
	if (resolved == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
166 167 168 169 170 171 172
		n = -ENOMEM;
		goto out_free;
	}

	sprintf(resolved, "%s%s", link, name);
	kfree(name);
	kfree(link);
Jeff Dike's avatar
Jeff Dike committed
173
	return resolved;
Linus Torvalds's avatar
Linus Torvalds committed
174 175 176 177

 out_free:
	kfree(name);
 out:
Jeff Dike's avatar
Jeff Dike committed
178
	return ERR_PTR(n);
Linus Torvalds's avatar
Linus Torvalds committed
179 180
}

181 182
static struct inode *hostfs_iget(struct super_block *sb)
{
Al Viro's avatar
Al Viro committed
183
	struct inode *inode = new_inode(sb);
184 185 186 187 188
	if (!inode)
		return ERR_PTR(-ENOMEM);
	return inode;
}

189
int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf)
Linus Torvalds's avatar
Linus Torvalds committed
190
{
Jeff Dike's avatar
Jeff Dike committed
191 192
	/*
	 * do_statfs uses struct statfs64 internally, but the linux kernel
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196 197 198 199 200 201 202
	 * struct statfs still has 32-bit versions for most of these fields,
	 * so we convert them here
	 */
	int err;
	long long f_blocks;
	long long f_bfree;
	long long f_bavail;
	long long f_files;
	long long f_ffree;

203
	err = do_statfs(dentry->d_sb->s_fs_info,
Linus Torvalds's avatar
Linus Torvalds committed
204 205
			&sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files,
			&f_ffree, &sf->f_fsid, sizeof(sf->f_fsid),
206
			&sf->f_namelen);
Jeff Dike's avatar
Jeff Dike committed
207
	if (err)
Jeff Dike's avatar
Jeff Dike committed
208
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
209 210 211 212 213 214
	sf->f_blocks = f_blocks;
	sf->f_bfree = f_bfree;
	sf->f_bavail = f_bavail;
	sf->f_files = f_files;
	sf->f_ffree = f_ffree;
	sf->f_type = HOSTFS_SUPER_MAGIC;
Jeff Dike's avatar
Jeff Dike committed
215
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
216 217 218 219 220 221
}

static struct inode *hostfs_alloc_inode(struct super_block *sb)
{
	struct hostfs_inode_info *hi;

222
	hi = kmalloc(sizeof(*hi), GFP_KERNEL);
Jeff Dike's avatar
Jeff Dike committed
223
	if (hi == NULL)
Jeff Dike's avatar
Jeff Dike committed
224
		return NULL;
225
	hi->fd = -1;
226
	hi->mode = 0;
Linus Torvalds's avatar
Linus Torvalds committed
227
	inode_init_once(&hi->vfs_inode);
Jeff Dike's avatar
Jeff Dike committed
228
	return &hi->vfs_inode;
Linus Torvalds's avatar
Linus Torvalds committed
229 230
}

231
static void hostfs_evict_inode(struct inode *inode)
Linus Torvalds's avatar
Linus Torvalds committed
232
{
233
	truncate_inode_pages(&inode->i_data, 0);
234
	clear_inode(inode);
Jeff Dike's avatar
Jeff Dike committed
235
	if (HOSTFS_I(inode)->fd != -1) {
Linus Torvalds's avatar
Linus Torvalds committed
236 237 238 239 240
		close_file(&HOSTFS_I(inode)->fd);
		HOSTFS_I(inode)->fd = -1;
	}
}

Nick Piggin's avatar
Nick Piggin committed
241
static void hostfs_i_callback(struct rcu_head *head)
Linus Torvalds's avatar
Linus Torvalds committed
242
{
Nick Piggin's avatar
Nick Piggin committed
243
	struct inode *inode = container_of(head, struct inode, i_rcu);
Linus Torvalds's avatar
Linus Torvalds committed
244 245
	kfree(HOSTFS_I(inode));
}
Nick Piggin's avatar
Nick Piggin committed
246 247 248 249 250

static void hostfs_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, hostfs_i_callback);
}
Linus Torvalds's avatar
Linus Torvalds committed
251

252
static int hostfs_show_options(struct seq_file *seq, struct dentry *root)
253
{
254
	const char *root_path = root->d_sb->s_fs_info;
255 256 257 258 259 260 261 262
	size_t offset = strlen(root_ino) + 1;

	if (strlen(root_path) > offset)
		seq_printf(seq, ",%s", root_path + offset);

	return 0;
}

263
static const struct super_operations hostfs_sbops = {
Linus Torvalds's avatar
Linus Torvalds committed
264 265
	.alloc_inode	= hostfs_alloc_inode,
	.destroy_inode	= hostfs_destroy_inode,
266
	.evict_inode	= hostfs_evict_inode,
Linus Torvalds's avatar
Linus Torvalds committed
267
	.statfs		= hostfs_statfs,
268
	.show_options	= hostfs_show_options,
Linus Torvalds's avatar
Linus Torvalds committed
269 270
};

Al Viro's avatar
Al Viro committed
271
int hostfs_readdir(struct file *file, struct dir_context *ctx)
Linus Torvalds's avatar
Linus Torvalds committed
272 273 274 275 276
{
	void *dir;
	char *name;
	unsigned long long next, ino;
	int error, len;
277
	unsigned int type;
Linus Torvalds's avatar
Linus Torvalds committed
278

279
	name = dentry_name(file->f_path.dentry);
Jeff Dike's avatar
Jeff Dike committed
280
	if (name == NULL)
Jeff Dike's avatar
Jeff Dike committed
281
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
282
	dir = open_dir(name, &error);
283
	__putname(name);
Jeff Dike's avatar
Jeff Dike committed
284
	if (dir == NULL)
Jeff Dike's avatar
Jeff Dike committed
285
		return -error;
Al Viro's avatar
Al Viro committed
286
	next = ctx->pos;
287
	while ((name = read_dir(dir, &next, &ino, &len, &type)) != NULL) {
Al Viro's avatar
Al Viro committed
288 289 290
		if (!dir_emit(ctx, name, len, ino, type))
			break;
		ctx->pos = next;
Linus Torvalds's avatar
Linus Torvalds committed
291 292
	}
	close_dir(dir);
Jeff Dike's avatar
Jeff Dike committed
293
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
294 295 296 297
}

int hostfs_file_open(struct inode *ino, struct file *file)
{
298
	static DEFINE_MUTEX(open_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
299
	char *name;
300
	fmode_t mode = 0;
301
	int err;
302
	int r = 0, w = 0, fd;
Linus Torvalds's avatar
Linus Torvalds committed
303 304

	mode = file->f_mode & (FMODE_READ | FMODE_WRITE);
Jeff Dike's avatar
Jeff Dike committed
305
	if ((mode & HOSTFS_I(ino)->mode) == mode)
Jeff Dike's avatar
Jeff Dike committed
306
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
307

308
	mode |= HOSTFS_I(ino)->mode;
Linus Torvalds's avatar
Linus Torvalds committed
309

310 311
retry:
	if (mode & FMODE_READ)
Linus Torvalds's avatar
Linus Torvalds committed
312
		r = 1;
313
	if (mode & FMODE_WRITE)
Linus Torvalds's avatar
Linus Torvalds committed
314
		w = 1;
Jeff Dike's avatar
Jeff Dike committed
315
	if (w)
Linus Torvalds's avatar
Linus Torvalds committed
316 317
		r = 1;

318
	name = dentry_name(file->f_path.dentry);
Jeff Dike's avatar
Jeff Dike committed
319
	if (name == NULL)
Jeff Dike's avatar
Jeff Dike committed
320
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
321 322

	fd = open_file(name, r, w, append);
323
	__putname(name);
Jeff Dike's avatar
Jeff Dike committed
324
	if (fd < 0)
Jeff Dike's avatar
Jeff Dike committed
325
		return fd;
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

	mutex_lock(&open_mutex);
	/* somebody else had handled it first? */
	if ((mode & HOSTFS_I(ino)->mode) == mode) {
		mutex_unlock(&open_mutex);
		return 0;
	}
	if ((mode | HOSTFS_I(ino)->mode) != mode) {
		mode |= HOSTFS_I(ino)->mode;
		mutex_unlock(&open_mutex);
		close_file(&fd);
		goto retry;
	}
	if (HOSTFS_I(ino)->fd == -1) {
		HOSTFS_I(ino)->fd = fd;
	} else {
		err = replace_file(fd, HOSTFS_I(ino)->fd);
		close_file(&fd);
		if (err < 0) {
			mutex_unlock(&open_mutex);
			return err;
		}
	}
	HOSTFS_I(ino)->mode = mode;
	mutex_unlock(&open_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
351

Jeff Dike's avatar
Jeff Dike committed
352
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
353 354
}

355 356 357 358 359 360 361
static int hostfs_file_release(struct inode *inode, struct file *file)
{
	filemap_write_and_wait(inode->i_mapping);

	return 0;
}

362
int hostfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
Linus Torvalds's avatar
Linus Torvalds committed
363
{
364 365 366 367 368 369 370 371 372 373 374 375
	struct inode *inode = file->f_mapping->host;
	int ret;

	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (ret)
		return ret;

	mutex_lock(&inode->i_mutex);
	ret = fsync_file(HOSTFS_I(inode)->fd, datasync);
	mutex_unlock(&inode->i_mutex);

	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
376 377
}

378
static const struct file_operations hostfs_file_fops = {
Linus Torvalds's avatar
Linus Torvalds committed
379
	.llseek		= generic_file_llseek,
380
	.read		= do_sync_read,
381
	.splice_read	= generic_file_splice_read,
Linus Torvalds's avatar
Linus Torvalds committed
382 383
	.aio_read	= generic_file_aio_read,
	.aio_write	= generic_file_aio_write,
384
	.write		= do_sync_write,
Linus Torvalds's avatar
Linus Torvalds committed
385 386
	.mmap		= generic_file_mmap,
	.open		= hostfs_file_open,
387
	.release	= hostfs_file_release,
Linus Torvalds's avatar
Linus Torvalds committed
388 389 390
	.fsync		= hostfs_fsync,
};

391
static const struct file_operations hostfs_dir_fops = {
Linus Torvalds's avatar
Linus Torvalds committed
392
	.llseek		= generic_file_llseek,
Al Viro's avatar
Al Viro committed
393
	.iterate	= hostfs_readdir,
Linus Torvalds's avatar
Linus Torvalds committed
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
	.read		= generic_read_dir,
};

int hostfs_writepage(struct page *page, struct writeback_control *wbc)
{
	struct address_space *mapping = page->mapping;
	struct inode *inode = mapping->host;
	char *buffer;
	unsigned long long base;
	int count = PAGE_CACHE_SIZE;
	int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
	int err;

	if (page->index >= end_index)
		count = inode->i_size & (PAGE_CACHE_SIZE-1);

	buffer = kmap(page);
	base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;

	err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count);
Jeff Dike's avatar
Jeff Dike committed
414
	if (err != count) {
Linus Torvalds's avatar
Linus Torvalds committed
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
		ClearPageUptodate(page);
		goto out;
	}

	if (base > inode->i_size)
		inode->i_size = base;

	if (PageError(page))
		ClearPageError(page);
	err = 0;

 out:
	kunmap(page);

	unlock_page(page);
	return err;
}

int hostfs_readpage(struct file *file, struct page *page)
{
	char *buffer;
	long long start;
	int err = 0;

	start = (long long) page->index << PAGE_CACHE_SHIFT;
	buffer = kmap(page);
	err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer,
			PAGE_CACHE_SIZE);
Jeff Dike's avatar
Jeff Dike committed
443 444
	if (err < 0)
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
445 446 447 448 449 450 451 452 453 454

	memset(&buffer[err], 0, PAGE_CACHE_SIZE - err);

	flush_dcache_page(page);
	SetPageUptodate(page);
	if (PageError(page)) ClearPageError(page);
	err = 0;
 out:
	kunmap(page);
	unlock_page(page);
Jeff Dike's avatar
Jeff Dike committed
455
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
456 457
}

458 459 460
int hostfs_write_begin(struct file *file, struct address_space *mapping,
			loff_t pos, unsigned len, unsigned flags,
			struct page **pagep, void **fsdata)
Linus Torvalds's avatar
Linus Torvalds committed
461
{
462
	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
Linus Torvalds's avatar
Linus Torvalds committed
463

464
	*pagep = grab_cache_page_write_begin(mapping, index, flags);
465 466 467
	if (!*pagep)
		return -ENOMEM;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
468 469
}

470 471 472
int hostfs_write_end(struct file *file, struct address_space *mapping,
			loff_t pos, unsigned len, unsigned copied,
			struct page *page, void *fsdata)
Linus Torvalds's avatar
Linus Torvalds committed
473 474
{
	struct inode *inode = mapping->host;
475 476 477
	void *buffer;
	unsigned from = pos & (PAGE_CACHE_SIZE - 1);
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
478 479

	buffer = kmap(page);
480 481
	err = write_file(FILE_HOSTFS_I(file)->fd, &pos, buffer + from, copied);
	kunmap(page);
482

483 484
	if (!PageUptodate(page) && err == PAGE_CACHE_SIZE)
		SetPageUptodate(page);
485

Jeff Dike's avatar
Jeff Dike committed
486 487
	/*
	 * If err > 0, write_file has added err to pos, so we are comparing
488 489 490 491 492 493
	 * i_size against the last byte written.
	 */
	if (err > 0 && (pos > inode->i_size))
		inode->i_size = pos;
	unlock_page(page);
	page_cache_release(page);
Linus Torvalds's avatar
Linus Torvalds committed
494

Jeff Dike's avatar
Jeff Dike committed
495
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
496 497
}

498
static const struct address_space_operations hostfs_aops = {
Linus Torvalds's avatar
Linus Torvalds committed
499 500
	.writepage 	= hostfs_writepage,
	.readpage	= hostfs_readpage,
501
	.set_page_dirty = __set_page_dirty_nobuffers,
502 503
	.write_begin	= hostfs_write_begin,
	.write_end	= hostfs_write_end,
Linus Torvalds's avatar
Linus Torvalds committed
504 505
};

506
static int read_name(struct inode *ino, char *name)
Linus Torvalds's avatar
Linus Torvalds committed
507
{
508 509 510 511 512
	dev_t rdev;
	struct hostfs_stat st;
	int err = stat_file(name, &st, -1);
	if (err)
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
513

514
	/* Reencode maj and min with the kernel encoding.*/
515
	rdev = MKDEV(st.maj, st.min);
Linus Torvalds's avatar
Linus Torvalds committed
516

517 518
	switch (st.mode & S_IFMT) {
	case S_IFLNK:
Al Viro's avatar
Al Viro committed
519
		ino->i_op = &hostfs_link_iops;
Linus Torvalds's avatar
Linus Torvalds committed
520
		break;
521 522 523
	case S_IFDIR:
		ino->i_op = &hostfs_dir_iops;
		ino->i_fop = &hostfs_dir_fops;
Linus Torvalds's avatar
Linus Torvalds committed
524
		break;
525 526 527 528 529 530
	case S_IFCHR:
	case S_IFBLK:
	case S_IFIFO:
	case S_IFSOCK:
		init_special_inode(ino, st.mode & S_IFMT, rdev);
		ino->i_op = &hostfs_iops;
Linus Torvalds's avatar
Linus Torvalds committed
531
		break;
532 533 534 535 536

	default:
		ino->i_op = &hostfs_iops;
		ino->i_fop = &hostfs_file_fops;
		ino->i_mapping->a_ops = &hostfs_aops;
Linus Torvalds's avatar
Linus Torvalds committed
537
	}
538 539 540

	ino->i_ino = st.ino;
	ino->i_mode = st.mode;
541
	set_nlink(ino, st.nlink);
542 543
	i_uid_write(ino, st.uid);
	i_gid_write(ino, st.gid);
544 545 546 547 548 549
	ino->i_atime = st.atime;
	ino->i_mtime = st.mtime;
	ino->i_ctime = st.ctime;
	ino->i_size = st.size;
	ino->i_blocks = st.blocks;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
550 551
}

Al Viro's avatar
Al Viro committed
552
int hostfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
553
		  bool excl)
Linus Torvalds's avatar
Linus Torvalds committed
554 555 556 557 558
{
	struct inode *inode;
	char *name;
	int error, fd;

559 560 561
	inode = hostfs_iget(dir->i_sb);
	if (IS_ERR(inode)) {
		error = PTR_ERR(inode);
Jeff Dike's avatar
Jeff Dike committed
562
		goto out;
563
	}
Linus Torvalds's avatar
Linus Torvalds committed
564 565

	error = -ENOMEM;
566
	name = dentry_name(dentry);
Jeff Dike's avatar
Jeff Dike committed
567
	if (name == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
568 569 570 571 572 573
		goto out_put;

	fd = file_create(name,
			 mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR,
			 mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP,
			 mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH);
574
	if (fd < 0)
Linus Torvalds's avatar
Linus Torvalds committed
575
		error = fd;
576
	else
577
		error = read_name(inode, name);
Linus Torvalds's avatar
Linus Torvalds committed
578

579
	__putname(name);
Jeff Dike's avatar
Jeff Dike committed
580
	if (error)
Linus Torvalds's avatar
Linus Torvalds committed
581 582 583 584 585
		goto out_put;

	HOSTFS_I(inode)->fd = fd;
	HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
	d_instantiate(dentry, inode);
Jeff Dike's avatar
Jeff Dike committed
586
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
587 588 589 590

 out_put:
	iput(inode);
 out:
Jeff Dike's avatar
Jeff Dike committed
591
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
592 593 594
}

struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
595
			     unsigned int flags)
Linus Torvalds's avatar
Linus Torvalds committed
596 597 598 599 600
{
	struct inode *inode;
	char *name;
	int err;

601 602 603
	inode = hostfs_iget(ino->i_sb);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
Linus Torvalds's avatar
Linus Torvalds committed
604
		goto out;
605
	}
Linus Torvalds's avatar
Linus Torvalds committed
606 607

	err = -ENOMEM;
608
	name = dentry_name(dentry);
Jeff Dike's avatar
Jeff Dike committed
609
	if (name == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
610 611 612
		goto out_put;

	err = read_name(inode, name);
613

614
	__putname(name);
Jeff Dike's avatar
Jeff Dike committed
615
	if (err == -ENOENT) {
Linus Torvalds's avatar
Linus Torvalds committed
616 617 618
		iput(inode);
		inode = NULL;
	}
Jeff Dike's avatar
Jeff Dike committed
619
	else if (err)
Linus Torvalds's avatar
Linus Torvalds committed
620 621 622
		goto out_put;

	d_add(dentry, inode);
Jeff Dike's avatar
Jeff Dike committed
623
	return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
624 625 626 627

 out_put:
	iput(inode);
 out:
Jeff Dike's avatar
Jeff Dike committed
628
	return ERR_PTR(err);
Linus Torvalds's avatar
Linus Torvalds committed
629 630 631 632
}

int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
{
Jeff Dike's avatar
Jeff Dike committed
633 634
	char *from_name, *to_name;
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
635

636
	if ((from_name = dentry_name(from)) == NULL)
Jeff Dike's avatar
Jeff Dike committed
637
		return -ENOMEM;
638
	to_name = dentry_name(to);
Jeff Dike's avatar
Jeff Dike committed
639
	if (to_name == NULL) {
640
		__putname(from_name);
Jeff Dike's avatar
Jeff Dike committed
641
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
642
	}
Jeff Dike's avatar
Jeff Dike committed
643
	err = link_file(to_name, from_name);
644 645
	__putname(from_name);
	__putname(to_name);
Jeff Dike's avatar
Jeff Dike committed
646
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
647 648 649 650 651 652 653
}

int hostfs_unlink(struct inode *ino, struct dentry *dentry)
{
	char *file;
	int err;

Jeff Dike's avatar
Jeff Dike committed
654
	if (append)
Jeff Dike's avatar
Jeff Dike committed
655
		return -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
656

Al Viro's avatar
Al Viro committed
657 658 659
	if ((file = dentry_name(dentry)) == NULL)
		return -ENOMEM;

Linus Torvalds's avatar
Linus Torvalds committed
660
	err = unlink_file(file);
661
	__putname(file);
Jeff Dike's avatar
Jeff Dike committed
662
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
663 664 665 666 667 668 669
}

int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to)
{
	char *file;
	int err;

670
	if ((file = dentry_name(dentry)) == NULL)
Jeff Dike's avatar
Jeff Dike committed
671
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
672
	err = make_symlink(file, to);
673
	__putname(file);
Jeff Dike's avatar
Jeff Dike committed
674
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
675 676
}

677
int hostfs_mkdir(struct inode *ino, struct dentry *dentry, umode_t mode)
Linus Torvalds's avatar
Linus Torvalds committed
678 679 680 681
{
	char *file;
	int err;

682
	if ((file = dentry_name(dentry)) == NULL)
Jeff Dike's avatar
Jeff Dike committed
683
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
684
	err = do_mkdir(file, mode);
685
	__putname(file);
Jeff Dike's avatar
Jeff Dike committed
686
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
687 688 689 690 691 692 693
}

int hostfs_rmdir(struct inode *ino, struct dentry *dentry)
{
	char *file;
	int err;

694
	if ((file = dentry_name(dentry)) == NULL)
Jeff Dike's avatar
Jeff Dike committed
695
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
696
	err = do_rmdir(file);
697
	__putname(file);
Jeff Dike's avatar
Jeff Dike committed
698
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
699 700
}

Al Viro's avatar
Al Viro committed
701
static int hostfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
Linus Torvalds's avatar
Linus Torvalds committed
702 703 704
{
	struct inode *inode;
	char *name;
705
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
706

707 708 709
	inode = hostfs_iget(dir->i_sb);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
Linus Torvalds's avatar
Linus Torvalds committed
710
		goto out;
711
	}
Linus Torvalds's avatar
Linus Torvalds committed
712 713

	err = -ENOMEM;
714
	name = dentry_name(dentry);
Jeff Dike's avatar
Jeff Dike committed
715
	if (name == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
716 717 718
		goto out_put;

	init_special_inode(inode, mode, dev);
719
	err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
720
	if (!err)
Linus Torvalds's avatar
Linus Torvalds committed
721 722 723
		goto out_free;

	err = read_name(inode, name);
724
	__putname(name);
725 726
	if (err)
		goto out_put;
Jeff Dike's avatar
Jeff Dike committed
727
	if (err)
Linus Torvalds's avatar
Linus Torvalds committed
728 729 730
		goto out_put;

	d_instantiate(dentry, inode);
Jeff Dike's avatar
Jeff Dike committed
731
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
732 733

 out_free:
734
	__putname(name);
Linus Torvalds's avatar
Linus Torvalds committed
735 736 737
 out_put:
	iput(inode);
 out:
Jeff Dike's avatar
Jeff Dike committed
738
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
739 740 741 742 743 744 745 746
}

int hostfs_rename(struct inode *from_ino, struct dentry *from,
		  struct inode *to_ino, struct dentry *to)
{
	char *from_name, *to_name;
	int err;

747
	if ((from_name = dentry_name(from)) == NULL)
Jeff Dike's avatar
Jeff Dike committed
748
		return -ENOMEM;
749
	if ((to_name = dentry_name(to)) == NULL) {
750
		__putname(from_name);
Jeff Dike's avatar
Jeff Dike committed
751
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
752 753
	}
	err = rename_file(from_name, to_name);
754 755
	__putname(from_name);
	__putname(to_name);
Jeff Dike's avatar
Jeff Dike committed
756
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
757 758
}

759
int hostfs_permission(struct inode *ino, int desired)
Linus Torvalds's avatar
Linus Torvalds committed
760 761 762 763
{
	char *name;
	int r = 0, w = 0, x = 0, err;

764
	if (desired & MAY_NOT_BLOCK)
765 766
		return -ECHILD;

Linus Torvalds's avatar
Linus Torvalds committed
767 768 769
	if (desired & MAY_READ) r = 1;
	if (desired & MAY_WRITE) w = 1;
	if (desired & MAY_EXEC) x = 1;
770
	name = inode_name(ino);
Jeff Dike's avatar
Jeff Dike committed
771 772
	if (name == NULL)
		return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
773 774

	if (S_ISCHR(ino->i_mode) || S_ISBLK(ino->i_mode) ||
Jeff Dike's avatar
Jeff Dike committed
775
	    S_ISFIFO(ino->i_mode) || S_ISSOCK(ino->i_mode))
Linus Torvalds's avatar
Linus Torvalds committed
776 777 778
		err = 0;
	else
		err = access_file(name, r, w, x);
779
	__putname(name);
Jeff Dike's avatar
Jeff Dike committed
780
	if (!err)
781
		err = generic_permission(ino, desired);
Linus Torvalds's avatar
Linus Torvalds committed
782 783 784 785 786
	return err;
}

int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
{
Christoph Hellwig's avatar
Christoph Hellwig committed
787
	struct inode *inode = dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
788 789 790 791
	struct hostfs_iattr attrs;
	char *name;
	int err;

Christoph Hellwig's avatar
Christoph Hellwig committed
792
	int fd = HOSTFS_I(inode)->fd;
793

Christoph Hellwig's avatar
Christoph Hellwig committed
794
	err = inode_change_ok(inode, attr);
Linus Torvalds's avatar
Linus Torvalds committed
795 796 797
	if (err)
		return err;

Jeff Dike's avatar
Jeff Dike committed
798
	if (append)
Linus Torvalds's avatar
Linus Torvalds committed
799 800 801
		attr->ia_valid &= ~ATTR_SIZE;

	attrs.ia_valid = 0;
Jeff Dike's avatar
Jeff Dike committed
802
	if (attr->ia_valid & ATTR_MODE) {
Linus Torvalds's avatar
Linus Torvalds committed
803 804 805
		attrs.ia_valid |= HOSTFS_ATTR_MODE;
		attrs.ia_mode = attr->ia_mode;
	}
Jeff Dike's avatar
Jeff Dike committed
806
	if (attr->ia_valid & ATTR_UID) {
Linus Torvalds's avatar
Linus Torvalds committed
807
		attrs.ia_valid |= HOSTFS_ATTR_UID;
808
		attrs.ia_uid = from_kuid(&init_user_ns, attr->ia_uid);
Linus Torvalds's avatar
Linus Torvalds committed
809
	}
Jeff Dike's avatar
Jeff Dike committed
810
	if (attr->ia_valid & ATTR_GID) {
Linus Torvalds's avatar
Linus Torvalds committed
811
		attrs.ia_valid |= HOSTFS_ATTR_GID;
812
		attrs.ia_gid = from_kgid(&init_user_ns, attr->ia_gid);
Linus Torvalds's avatar
Linus Torvalds committed
813
	}
Jeff Dike's avatar
Jeff Dike committed
814
	if (attr->ia_valid & ATTR_SIZE) {
Linus Torvalds's avatar
Linus Torvalds committed
815 816 817
		attrs.ia_valid |= HOSTFS_ATTR_SIZE;
		attrs.ia_size = attr->ia_size;
	}
Jeff Dike's avatar
Jeff Dike committed
818
	if (attr->ia_valid & ATTR_ATIME) {
Linus Torvalds's avatar
Linus Torvalds committed
819 820 821
		attrs.ia_valid |= HOSTFS_ATTR_ATIME;
		attrs.ia_atime = attr->ia_atime;
	}
Jeff Dike's avatar
Jeff Dike committed
822
	if (attr->ia_valid & ATTR_MTIME) {
Linus Torvalds's avatar
Linus Torvalds committed
823 824 825
		attrs.ia_valid |= HOSTFS_ATTR_MTIME;
		attrs.ia_mtime = attr->ia_mtime;
	}
Jeff Dike's avatar
Jeff Dike committed
826
	if (attr->ia_valid & ATTR_CTIME) {
Linus Torvalds's avatar
Linus Torvalds committed
827 828 829
		attrs.ia_valid |= HOSTFS_ATTR_CTIME;
		attrs.ia_ctime = attr->ia_ctime;
	}
Jeff Dike's avatar
Jeff Dike committed
830
	if (attr->ia_valid & ATTR_ATIME_SET) {
Linus Torvalds's avatar
Linus Torvalds committed
831 832
		attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET;
	}
Jeff Dike's avatar
Jeff Dike committed
833
	if (attr->ia_valid & ATTR_MTIME_SET) {
Linus Torvalds's avatar
Linus Torvalds committed
834 835
		attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET;
	}
836
	name = dentry_name(dentry);
Jeff Dike's avatar
Jeff Dike committed
837
	if (name == NULL)
Jeff Dike's avatar
Jeff Dike committed
838
		return -ENOMEM;
839
	err = set_attr(name, &attrs, fd);
840
	__putname(name);
Jeff Dike's avatar
Jeff Dike committed
841
	if (err)
Jeff Dike's avatar
Jeff Dike committed
842
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
843

Christoph Hellwig's avatar
Christoph Hellwig committed
844
	if ((attr->ia_valid & ATTR_SIZE) &&
845
	    attr->ia_size != i_size_read(inode))
846
		truncate_setsize(inode, attr->ia_size);
Christoph Hellwig's avatar
Christoph Hellwig committed
847 848 849 850

	setattr_copy(inode, attr);
	mark_inode_dirty(inode);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
851 852
}

853
static const struct inode_operations hostfs_iops = {
Linus Torvalds's avatar
Linus Torvalds committed
854 855 856 857
	.permission	= hostfs_permission,
	.setattr	= hostfs_setattr,
};

858
static const struct inode_operations hostfs_dir_iops = {
Linus Torvalds's avatar
Linus Torvalds committed
859 860 861 862 863 864 865 866 867 868 869 870 871
	.create		= hostfs_create,
	.lookup		= hostfs_lookup,
	.link		= hostfs_link,
	.unlink		= hostfs_unlink,
	.symlink	= hostfs_symlink,
	.mkdir		= hostfs_mkdir,
	.rmdir		= hostfs_rmdir,
	.mknod		= hostfs_mknod,
	.rename		= hostfs_rename,
	.permission	= hostfs_permission,
	.setattr	= hostfs_setattr,
};

Al Viro's avatar
Al Viro committed
872 873 874 875 876 877 878
static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
	char *link = __getname();
	if (link) {
		char *path = dentry_name(dentry);
		int err = -ENOMEM;
		if (path) {
Al Viro's avatar
Al Viro committed
879
			err = hostfs_do_readlink(path, link, PATH_MAX);
Al Viro's avatar
Al Viro committed
880 881
			if (err == PATH_MAX)
				err = -E2BIG;
882
			__putname(path);
Al Viro's avatar
Al Viro committed
883 884 885 886 887 888 889
		}
		if (err < 0) {
			__putname(link);
			link = ERR_PTR(err);
		}
	} else {
		link = ERR_PTR(-ENOMEM);
Linus Torvalds's avatar
Linus Torvalds committed
890
	}
Al Viro's avatar
Al Viro committed
891 892 893 894 895 896 897 898 899 900

	nd_set_link(nd, link);
	return NULL;
}

static void hostfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
{
	char *s = nd_get_link(nd);
	if (!IS_ERR(s))
		__putname(s);
Linus Torvalds's avatar
Linus Torvalds committed
901 902
}

Al Viro's avatar
Al Viro committed
903 904 905 906
static const struct inode_operations hostfs_link_iops = {
	.readlink	= generic_readlink,
	.follow_link	= hostfs_follow_link,
	.put_link	= hostfs_put_link,
Linus Torvalds's avatar
Linus Torvalds committed
907 908 909 910 911
};

static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
{
	struct inode *root_inode;
912
	char *host_root_path, *req_root = d;
Linus Torvalds's avatar
Linus Torvalds committed
913 914 915 916 917 918
	int err;

	sb->s_blocksize = 1024;
	sb->s_blocksize_bits = 10;
	sb->s_magic = HOSTFS_SUPER_MAGIC;
	sb->s_op = &hostfs_sbops;
919
	sb->s_d_op = &simple_dentry_operations;
920
	sb->s_maxbytes = MAX_LFS_FILESIZE;
Linus Torvalds's avatar
Linus Torvalds committed
921

922
	/* NULL is printed as <NULL> by sprintf: avoid that. */
923 924
	if (req_root == NULL)
		req_root = "";
Linus Torvalds's avatar
Linus Torvalds committed
925 926

	err = -ENOMEM;
927 928
	sb->s_fs_info = host_root_path =
		kmalloc(strlen(root_ino) + strlen(req_root) + 2, GFP_KERNEL);
Jeff Dike's avatar
Jeff Dike committed
929
	if (host_root_path == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
930 931
		goto out;

932
	sprintf(host_root_path, "%s/%s", root_ino, req_root);
Linus Torvalds's avatar
Linus Torvalds committed
933

Al Viro's avatar
Al Viro committed
934 935
	root_inode = new_inode(sb);
	if (!root_inode)
936
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
937

938 939 940
	err = read_name(root_inode, host_root_path);
	if (err)
		goto out_put;
Al Viro's avatar
Al Viro committed
941

942
	if (S_ISLNK(root_inode->i_mode)) {
Al Viro's avatar
Al Viro committed
943 944 945 946 947 948
		char *name = follow_link(host_root_path);
		if (IS_ERR(name))
			err = PTR_ERR(name);
		else
			err = read_name(root_inode, name);
		kfree(name);
949 950
		if (err)
			goto out_put;
Al Viro's avatar
Al Viro committed
951
	}
Linus Torvalds's avatar
Linus Torvalds committed
952 953

	err = -ENOMEM;
954
	sb->s_root = d_make_root(root_inode);
Jeff Dike's avatar
Jeff Dike committed
955
	if (sb->s_root == NULL)
956
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
957

Jeff Dike's avatar
Jeff Dike committed
958
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
959

Jeff Dike's avatar
Jeff Dike committed
960 961 962 963
out_put:
	iput(root_inode);
out:
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
964 965
}

Al Viro's avatar
Al Viro committed
966
static struct dentry *hostfs_read_sb(struct file_system_type *type,
967
			  int flags, const char *dev_name,
Al Viro's avatar
Al Viro committed
968
			  void *data)
Linus Torvalds's avatar
Linus Torvalds committed
969
{
Al Viro's avatar
Al Viro committed
970
	return mount_nodev(type, flags, data, hostfs_fill_sb_common);
Linus Torvalds's avatar
Linus Torvalds committed
971 972
}

973 974 975 976 977 978
static void hostfs_kill_sb(struct super_block *s)
{
	kill_anon_super(s);
	kfree(s->s_fs_info);
}

Linus Torvalds's avatar
Linus Torvalds committed
979 980 981
static struct file_system_type hostfs_type = {
	.owner 		= THIS_MODULE,
	.name 		= "hostfs",
Al Viro's avatar
Al Viro committed
982
	.mount	 	= hostfs_read_sb,
983
	.kill_sb	= hostfs_kill_sb,
Linus Torvalds's avatar
Linus Torvalds committed
984 985
	.fs_flags 	= 0,
};
986
MODULE_ALIAS_FS("hostfs");
Linus Torvalds's avatar
Linus Torvalds committed
987 988 989

static int __init init_hostfs(void)
{
Jeff Dike's avatar
Jeff Dike committed
990
	return register_filesystem(&hostfs_type);
Linus Torvalds's avatar
Linus Torvalds committed
991 992 993 994 995 996 997 998 999 1000
}

static void __exit exit_hostfs(void)
{
	unregister_filesystem(&hostfs_type);
}

module_init(init_hostfs)
module_exit(exit_hostfs)
MODULE_LICENSE("GPL");