namei.c 17.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11
/*
 *  linux/fs/sysv/namei.c
 *
 *  minix/namei.c
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  coh/namei.c
 *  Copyright (C) 1993  Pascal Haible, Bruno Haible
 *
 *  sysv/namei.c
 *  Copyright (C) 1993  Bruno Haible
12
 *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
Linus Torvalds's avatar
Linus Torvalds committed
13
 */
Linus Torvalds's avatar
Linus Torvalds committed
14

Linus Torvalds's avatar
Linus Torvalds committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sysv_fs.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>

/* compare strings: name[0..len-1] (not zero-terminated) and
 * buffer[0..] (filled with zeroes up to buffer[0..maxlen-1])
 */
static inline int namecompare(int len, int maxlen,
	const char * name, const char * buffer)
{
Linus Torvalds's avatar
Linus Torvalds committed
30 31 32 33 34
	if (len > maxlen)
		return 0;
	if (len < maxlen && buffer[len])
		return 0;
	return !memcmp(name, buffer, len);
Linus Torvalds's avatar
Linus Torvalds committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
}

/*
 * ok, we cannot use strncmp, as the name is not in our data space. [Now it is!]
 * Thus we'll have to use sysv_match. No big problem. Match also makes
 * some sanity tests.
 *
 * NOTE! unlike strncmp, sysv_match returns 1 for success, 0 for failure.
 */
static int sysv_match(int len, const char * name, struct sysv_dir_entry * de)
{
	if (!de->inode || len > SYSV_NAMELEN)
		return 0;
	/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
	if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
		return 1;
Linus Torvalds's avatar
Linus Torvalds committed
51
	return namecompare(len, SYSV_NAMELEN, name, de->name);
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
}

/*
 *	sysv_find_entry()
 *
 * finds an entry in the specified directory with the wanted name. It
 * returns the cache buffer in which the entry was found, and the entry
 * itself (as a parameter - res_dir). It does NOT read the inode of the
 * entry - you'll have to do that yourself if you want to.
 */
static struct buffer_head * sysv_find_entry(struct inode * dir,
	const char * name, int namelen, struct sysv_dir_entry ** res_dir)
{
	struct super_block * sb;
	unsigned long pos, block, offset; /* pos = block * block_size + offset */
	struct buffer_head * bh;

	*res_dir = NULL;
	if (!dir)
		return NULL;
	sb = dir->i_sb;
Linus Torvalds's avatar
Linus Torvalds committed
73
	if (namelen > SYSV_NAMELEN) {
Linus Torvalds's avatar
Linus Torvalds committed
74 75 76 77
		if (sb->sv_truncate)
			namelen = SYSV_NAMELEN;
		else
			return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
78
	}
Linus Torvalds's avatar
Linus Torvalds committed
79 80 81 82
	bh = NULL;
	pos = block = offset = 0;
	while (pos < dir->i_size) {
		if (!bh) {
Linus Torvalds's avatar
Linus Torvalds committed
83
			bh = sysv_file_bread(dir, block, 0);
Linus Torvalds's avatar
Linus Torvalds committed
84 85 86 87 88 89 90
			if (!bh) {
				/* offset = 0; */ block++;
				pos += sb->sv_block_size;
				continue;
			}
		}
		if (sysv_match(namelen, name,
Linus Torvalds's avatar
Linus Torvalds committed
91
			       *res_dir = (struct sysv_dir_entry *) (bh->b_data + offset) ))
Linus Torvalds's avatar
Linus Torvalds committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105
			return bh;
		pos += SYSV_DIRSIZE;
		offset += SYSV_DIRSIZE;
		if (offset < sb->sv_block_size)
			continue;
		brelse(bh);
		bh = NULL;
		offset = 0; block++;
	}
	brelse(bh);
	*res_dir = NULL;
	return NULL;
}

Linus Torvalds's avatar
Linus Torvalds committed
106
int sysv_lookup(struct inode * dir, struct dentry * dentry)
Linus Torvalds's avatar
Linus Torvalds committed
107
{
Linus Torvalds's avatar
Linus Torvalds committed
108
	struct inode * inode = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
109 110 111 112 113 114 115 116
	struct sysv_dir_entry * de;
	struct buffer_head * bh;

	if (!dir)
		return -ENOENT;
	if (!S_ISDIR(dir->i_mode)) {
		return -ENOENT;
	}
Linus Torvalds's avatar
Linus Torvalds committed
117 118 119 120 121
	bh = sysv_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);

	if (bh) {
		int ino = de->inode;
		brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
122
		inode = iget(dir->i_sb, ino);
Linus Torvalds's avatar
Linus Torvalds committed
123
	
Linus Torvalds's avatar
Linus Torvalds committed
124 125 126
		if (!inode) 
			return -EACCES;
	}
Linus Torvalds's avatar
Linus Torvalds committed
127
	d_add(dentry, inode);
Linus Torvalds's avatar
Linus Torvalds committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
	return 0;
}

/*
 *	sysv_add_entry()
 *
 * adds a file entry to the specified directory, returning a possible
 * error value if it fails.
 *
 * NOTE!! The inode part of 'de' is left at 0 - which means you
 * may not sleep between calling this and putting something into
 * the entry, as someone else might have used it while you slept.
 */
static int sysv_add_entry(struct inode * dir,
	const char * name, int namelen,
	struct buffer_head ** res_buf,
	struct sysv_dir_entry ** res_dir)
{
	struct super_block * sb;
	int i;
	unsigned long pos, block, offset; /* pos = block * block_size + offset */
	struct buffer_head * bh;
	struct sysv_dir_entry * de;

	*res_buf = NULL;
	*res_dir = NULL;
	if (!dir)
		return -ENOENT;
	sb = dir->i_sb;
Linus Torvalds's avatar
Linus Torvalds committed
157
	if (namelen > SYSV_NAMELEN) {
Linus Torvalds's avatar
Linus Torvalds committed
158 159 160 161
		if (sb->sv_truncate)
			namelen = SYSV_NAMELEN;
		else
			return -ENAMETOOLONG;
Linus Torvalds's avatar
Linus Torvalds committed
162
	}
Linus Torvalds's avatar
Linus Torvalds committed
163 164 165 166 167 168
	if (!namelen)
		return -ENOENT;
	bh = NULL;
	pos = block = offset = 0;
	while (1) {
		if (!bh) {
Linus Torvalds's avatar
Linus Torvalds committed
169
			bh = sysv_file_bread(dir, block, 1);
Linus Torvalds's avatar
Linus Torvalds committed
170 171 172
			if (!bh)
				return -ENOSPC;
		}
Linus Torvalds's avatar
Linus Torvalds committed
173
		de = (struct sysv_dir_entry *) (bh->b_data + offset);
Linus Torvalds's avatar
Linus Torvalds committed
174 175 176 177 178
		pos += SYSV_DIRSIZE;
		offset += SYSV_DIRSIZE;
		if (pos > dir->i_size) {
			de->inode = 0;
			dir->i_size = pos;
Linus Torvalds's avatar
Linus Torvalds committed
179
			mark_inode_dirty(dir);
Linus Torvalds's avatar
Linus Torvalds committed
180 181 182 183 184 185 186 187
		}
		if (de->inode) {
			if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) {
				brelse(bh);
				return -EEXIST;
			}
		} else {
			dir->i_mtime = dir->i_ctime = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
188
			mark_inode_dirty(dir);
Linus Torvalds's avatar
Linus Torvalds committed
189 190
			for (i = 0; i < SYSV_NAMELEN ; i++)
				de->name[i] = (i < namelen) ? name[i] : 0;
Linus Torvalds's avatar
Linus Torvalds committed
191
			mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
192 193 194 195 196 197 198 199 200 201 202 203 204
			*res_dir = de;
			break;
		}
		if (offset < sb->sv_block_size)
			continue;
		brelse(bh);
		bh = NULL;
		offset = 0; block++;
	}
	*res_buf = bh;
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
205
int sysv_create(struct inode * dir, struct dentry * dentry, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
206 207 208 209 210 211 212 213 214
{
	int error;
	struct inode * inode;
	struct buffer_head * bh;
	struct sysv_dir_entry * de;

	if (!dir)
		return -ENOENT;
	inode = sysv_new_inode(dir);
Linus Torvalds's avatar
Linus Torvalds committed
215
	if (!inode) 
Linus Torvalds's avatar
Linus Torvalds committed
216
		return -ENOSPC;
Linus Torvalds's avatar
Linus Torvalds committed
217
	inode->i_op = &sysv_file_inode_operations;
Linus Torvalds's avatar
Linus Torvalds committed
218
	inode->i_mode = mode;
Linus Torvalds's avatar
Linus Torvalds committed
219
	mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
220
	error = sysv_add_entry(dir, dentry->d_name.name,
Linus Torvalds's avatar
Linus Torvalds committed
221
			       dentry->d_name.len, &bh, &de);
Linus Torvalds's avatar
Linus Torvalds committed
222 223
	if (error) {
		inode->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
224
		mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
225 226 227 228
		iput(inode);
		return error;
	}
	de->inode = inode->i_ino;
Linus Torvalds's avatar
Linus Torvalds committed
229
	mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
230
	brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
231
	d_instantiate(dentry, inode);
Linus Torvalds's avatar
Linus Torvalds committed
232 233 234
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
235
int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
Linus Torvalds's avatar
Linus Torvalds committed
236 237 238 239 240 241 242 243
{
	int error;
	struct inode * inode;
	struct buffer_head * bh;
	struct sysv_dir_entry * de;

	if (!dir)
		return -ENOENT;
Linus Torvalds's avatar
Linus Torvalds committed
244 245
	bh = sysv_find_entry(dir, dentry->d_name.name,
			     dentry->d_name.len, &de);
Linus Torvalds's avatar
Linus Torvalds committed
246 247 248 249 250
	if (bh) {
		brelse(bh);
		return -EEXIST;
	}
	inode = sysv_new_inode(dir);
Linus Torvalds's avatar
Linus Torvalds committed
251
	if (!inode)
Linus Torvalds's avatar
Linus Torvalds committed
252
		return -ENOSPC;
Linus Torvalds's avatar
Linus Torvalds committed
253
	inode->i_uid = current->fsuid;
Linus Torvalds's avatar
Linus Torvalds committed
254 255 256
	inode->i_mode = mode;
	inode->i_op = NULL;
	if (S_ISREG(inode->i_mode))
Linus Torvalds's avatar
Linus Torvalds committed
257
		inode->i_op = &sysv_file_inode_operations;
Linus Torvalds's avatar
Linus Torvalds committed
258 259 260 261 262 263 264
	else if (S_ISCHR(inode->i_mode))
		inode->i_op = &chrdev_inode_operations;
	else if (S_ISBLK(inode->i_mode))
		inode->i_op = &blkdev_inode_operations;
	else if (S_ISFIFO(inode->i_mode))
		init_fifo(inode);
	if (S_ISBLK(mode) || S_ISCHR(mode))
Linus Torvalds's avatar
Linus Torvalds committed
265
		inode->i_rdev = to_kdev_t(rdev);
Linus Torvalds's avatar
Linus Torvalds committed
266
	mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
267 268
	error = sysv_add_entry(dir, dentry->d_name.name, 
			       dentry->d_name.len, &bh, &de);
Linus Torvalds's avatar
Linus Torvalds committed
269 270
	if (error) {
		inode->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
271
		mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
272 273 274 275
		iput(inode);
		return error;
	}
	de->inode = inode->i_ino;
Linus Torvalds's avatar
Linus Torvalds committed
276
	mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
277
	brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
278
	d_instantiate(dentry, inode);
Linus Torvalds's avatar
Linus Torvalds committed
279 280 281
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
282
int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode)
Linus Torvalds's avatar
Linus Torvalds committed
283 284 285 286 287 288
{
	int error;
	struct inode * inode;
	struct buffer_head * bh, *dir_block;
	struct sysv_dir_entry * de;

Linus Torvalds's avatar
Linus Torvalds committed
289
	if (!dir)
Linus Torvalds's avatar
Linus Torvalds committed
290
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
291 292
	bh = sysv_find_entry(dir, dentry->d_name.name,
                              dentry->d_name.len, &de);
Linus Torvalds's avatar
Linus Torvalds committed
293 294 295 296
	if (bh) {
		brelse(bh);
		return -EEXIST;
	}
Linus Torvalds's avatar
Linus Torvalds committed
297
	if (dir->i_nlink >= dir->i_sb->sv_link_max) 
Linus Torvalds's avatar
Linus Torvalds committed
298 299
		return -EMLINK;
	inode = sysv_new_inode(dir);
Linus Torvalds's avatar
Linus Torvalds committed
300
	if (!inode)
Linus Torvalds's avatar
Linus Torvalds committed
301 302 303
		return -ENOSPC;
	inode->i_op = &sysv_dir_inode_operations;
	inode->i_size = 2 * SYSV_DIRSIZE;
Linus Torvalds's avatar
Linus Torvalds committed
304
	dir_block = sysv_file_bread(inode,0,1);
Linus Torvalds's avatar
Linus Torvalds committed
305 306
	if (!dir_block) {
		inode->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
307
		mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
308 309 310
		iput(inode);
		return -ENOSPC;
	}
Linus Torvalds's avatar
Linus Torvalds committed
311
	de = (struct sysv_dir_entry *) (dir_block->b_data + 0*SYSV_DIRSIZE);
Linus Torvalds's avatar
Linus Torvalds committed
312 313
	de->inode = inode->i_ino;
	strcpy(de->name,"."); /* rest of de->name is zero, see sysv_new_block */
Linus Torvalds's avatar
Linus Torvalds committed
314
	de = (struct sysv_dir_entry *) (dir_block->b_data + 1*SYSV_DIRSIZE);
Linus Torvalds's avatar
Linus Torvalds committed
315 316 317
	de->inode = dir->i_ino;
	strcpy(de->name,".."); /* rest of de->name is zero, see sysv_new_block */
	inode->i_nlink = 2;
Linus Torvalds's avatar
Linus Torvalds committed
318
	mark_buffer_dirty(dir_block, 1);
Linus Torvalds's avatar
Linus Torvalds committed
319
	brelse(dir_block);
Linus Torvalds's avatar
Linus Torvalds committed
320
	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
Linus Torvalds's avatar
Linus Torvalds committed
321 322
	if (dir->i_mode & S_ISGID)
		inode->i_mode |= S_ISGID;
Linus Torvalds's avatar
Linus Torvalds committed
323
	mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
324 325 326
	error = sysv_add_entry(dir, dentry->d_name.name,
                               dentry->d_name.len, &bh, &de);
 	if (error) {
Linus Torvalds's avatar
Linus Torvalds committed
327 328 329 330 331
		inode->i_nlink=0;
		iput(inode);
		return error;
	}
	de->inode = inode->i_ino;
Linus Torvalds's avatar
Linus Torvalds committed
332
	mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
333
	dir->i_nlink++;
Linus Torvalds's avatar
Linus Torvalds committed
334
	mark_inode_dirty(dir);
Linus Torvalds's avatar
Linus Torvalds committed
335
	brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
336 337
        d_instantiate(dentry, inode);
 	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
}

/*
 * routine to check that the specified directory is empty (for rmdir)
 */
static int empty_dir(struct inode * inode)
{
	struct super_block * sb;
	unsigned long pos, block, offset; /* pos = block * block_size + offset */
	struct buffer_head * bh;
	struct sysv_dir_entry * de;

	if (!inode)
		return 1;
	block = 0;
	bh = NULL;
	pos = offset = 2*SYSV_DIRSIZE;
	if (inode->i_size % SYSV_DIRSIZE)
		goto bad_dir;
	if (inode->i_size < pos)
		goto bad_dir;
Linus Torvalds's avatar
Linus Torvalds committed
359
	bh = sysv_file_bread(inode, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
360 361
	if (!bh)
		goto bad_dir;
Linus Torvalds's avatar
Linus Torvalds committed
362
	de = (struct sysv_dir_entry *) (bh->b_data + 0*SYSV_DIRSIZE);
Linus Torvalds's avatar
Linus Torvalds committed
363 364
	if (!de->inode || strcmp(de->name,"."))
		goto bad_dir;
Linus Torvalds's avatar
Linus Torvalds committed
365
	de = (struct sysv_dir_entry *) (bh->b_data + 1*SYSV_DIRSIZE);
Linus Torvalds's avatar
Linus Torvalds committed
366 367 368 369 370
	if (!de->inode || strcmp(de->name,".."))
		goto bad_dir;
	sb = inode->i_sb;
	while (pos < inode->i_size) {
		if (!bh) {
Linus Torvalds's avatar
Linus Torvalds committed
371
			bh = sysv_file_bread(inode, block, 0);
Linus Torvalds's avatar
Linus Torvalds committed
372 373 374 375 376 377
			if (!bh) {
				/* offset = 0; */ block++;
				pos += sb->sv_block_size;
				continue;
			}
		}
Linus Torvalds's avatar
Linus Torvalds committed
378
		de = (struct sysv_dir_entry *) (bh->b_data + offset);
Linus Torvalds's avatar
Linus Torvalds committed
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
		pos += SYSV_DIRSIZE;
		offset += SYSV_DIRSIZE;
		if (de->inode) {
			brelse(bh);
			return 0;
		}
		if (offset < sb->sv_block_size)
			continue;
		brelse(bh);
		bh = NULL;
		offset = 0; block++;
	}
	brelse(bh);
	return 1;
bad_dir:
	brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
395 396
	printk("Bad directory on device %s\n",
	       kdevname(inode->i_dev));
Linus Torvalds's avatar
Linus Torvalds committed
397 398 399
	return 1;
}

Linus Torvalds's avatar
Linus Torvalds committed
400
int sysv_rmdir(struct inode * dir, struct dentry * dentry)
Linus Torvalds's avatar
Linus Torvalds committed
401 402 403 404 405 406 407
{
	int retval;
	struct inode * inode;
	struct buffer_head * bh;
	struct sysv_dir_entry * de;

	inode = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
408 409
	bh = sysv_find_entry(dir, dentry->d_name.name,
                             dentry->d_name.len, &de);
Linus Torvalds's avatar
Linus Torvalds committed
410 411 412
	retval = -ENOENT;
	if (!bh)
		goto end_rmdir;
Linus Torvalds's avatar
Linus Torvalds committed
413 414
	inode = dentry->d_inode;

Linus Torvalds's avatar
Linus Torvalds committed
415 416 417 418 419 420 421 422
	if (!empty_dir(inode)) {
		retval = -ENOTEMPTY;
		goto end_rmdir;
	}
	if (de->inode != inode->i_ino) {
		retval = -ENOENT;
		goto end_rmdir;
	}
Linus Torvalds's avatar
Linus Torvalds committed
423
	if (!list_empty(&dentry->d_hash)) {
Linus Torvalds's avatar
Linus Torvalds committed
424 425 426 427
		retval = -EBUSY;
		goto end_rmdir;
	}
	if (inode->i_nlink != 2)
Linus Torvalds's avatar
Linus Torvalds committed
428
		printk("empty directory has nlink!=2 (%d)\n", inode->i_nlink);
Linus Torvalds's avatar
Linus Torvalds committed
429
	de->inode = 0;
Linus Torvalds's avatar
Linus Torvalds committed
430
	mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
431
	inode->i_nlink=0;
Linus Torvalds's avatar
Linus Torvalds committed
432
	mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
433 434
	dir->i_nlink--;
	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
435
	mark_inode_dirty(dir);
Linus Torvalds's avatar
Linus Torvalds committed
436
	d_delete(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
437 438 439 440 441 442
	retval = 0;
end_rmdir:
	brelse(bh);
	return retval;
}

Linus Torvalds's avatar
Linus Torvalds committed
443
int sysv_unlink(struct inode * dir, struct dentry * dentry)
Linus Torvalds's avatar
Linus Torvalds committed
444 445 446 447 448 449 450 451 452
{
	int retval;
	struct inode * inode;
	struct buffer_head * bh;
	struct sysv_dir_entry * de;

repeat:
	retval = -ENOENT;
	inode = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
453 454
	bh = sysv_find_entry(dir, dentry->d_name.name,
                             dentry->d_name.len, &de);
Linus Torvalds's avatar
Linus Torvalds committed
455 456
	if (!bh)
		goto end_unlink;
Linus Torvalds's avatar
Linus Torvalds committed
457 458
	inode = dentry->d_inode;

Linus Torvalds's avatar
Linus Torvalds committed
459 460 461 462 463 464 465 466 467 468 469 470
	retval = -EPERM;
	if (de->inode != inode->i_ino) {
		brelse(bh);
		current->counter = 0;
		schedule();
		goto repeat;
	}
	if (de->inode != inode->i_ino) {
		retval = -ENOENT;
		goto end_unlink;
	}
	if (!inode->i_nlink) {
Linus Torvalds's avatar
Linus Torvalds committed
471
		printk("Deleting nonexistent file (%s:%lu), %d\n",
Linus Torvalds's avatar
Linus Torvalds committed
472
		        kdevname(inode->i_dev), inode->i_ino, inode->i_nlink);
Linus Torvalds's avatar
Linus Torvalds committed
473 474 475
		inode->i_nlink=1;
	}
	de->inode = 0;
Linus Torvalds's avatar
Linus Torvalds committed
476
	mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
477
	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
478
	mark_inode_dirty(dir);
Linus Torvalds's avatar
Linus Torvalds committed
479 480
	inode->i_nlink--;
	inode->i_ctime = dir->i_ctime;
Linus Torvalds's avatar
Linus Torvalds committed
481
	mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
482
	d_delete(dentry);
Linus Torvalds's avatar
Linus Torvalds committed
483 484 485 486 487 488
	retval = 0;
end_unlink:
	brelse(bh);
	return retval;
}

Linus Torvalds's avatar
Linus Torvalds committed
489 490
int sysv_symlink(struct inode * dir, struct dentry * dentry, 
		 const char * symname)
Linus Torvalds's avatar
Linus Torvalds committed
491 492 493 494 495 496 497 498 499 500
{
	struct sysv_dir_entry * de;
	struct inode * inode;
	struct buffer_head * name_block;
	char * name_block_data;
	struct super_block * sb;
	int i;
	char c;
	struct buffer_head * bh;

Linus Torvalds's avatar
Linus Torvalds committed
501
	if (!(inode = sysv_new_inode(dir)))
Linus Torvalds's avatar
Linus Torvalds committed
502
		return -ENOSPC;
Linus Torvalds's avatar
Linus Torvalds committed
503

Linus Torvalds's avatar
Linus Torvalds committed
504 505
	inode->i_mode = S_IFLNK | 0777;
	inode->i_op = &sysv_symlink_inode_operations;
Linus Torvalds's avatar
Linus Torvalds committed
506
	name_block = sysv_file_bread(inode, 0, 1);
Linus Torvalds's avatar
Linus Torvalds committed
507 508
	if (!name_block) {
		inode->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
509
		mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
510 511 512 513
		iput(inode);
		return -ENOSPC;
	}
	sb = inode->i_sb;
Linus Torvalds's avatar
Linus Torvalds committed
514
	name_block_data = name_block->b_data;
Linus Torvalds's avatar
Linus Torvalds committed
515 516 517 518
	i = 0;
	while (i < sb->sv_block_size_1 && (c = *(symname++)))
		name_block_data[i++] = c;
	name_block_data[i] = 0;
Linus Torvalds's avatar
Linus Torvalds committed
519
	mark_buffer_dirty(name_block, 1);
Linus Torvalds's avatar
Linus Torvalds committed
520 521
	brelse(name_block);
	inode->i_size = i;
Linus Torvalds's avatar
Linus Torvalds committed
522
	mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
523 524
	bh = sysv_find_entry(dir, dentry->d_name.name,
                             dentry->d_name.len, &de);
Linus Torvalds's avatar
Linus Torvalds committed
525 526
	if (bh) {
		inode->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
527
		mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
528 529 530 531
		iput(inode);
		brelse(bh);
		return -EEXIST;
	}
Linus Torvalds's avatar
Linus Torvalds committed
532 533
	i = sysv_add_entry(dir, dentry->d_name.name,
                           dentry->d_name.len, &bh, &de);
Linus Torvalds's avatar
Linus Torvalds committed
534 535
	if (i) {
		inode->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
536
		mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
537 538 539 540
		iput(inode);
		return i;
	}
	de->inode = inode->i_ino;
Linus Torvalds's avatar
Linus Torvalds committed
541
	mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
542
	brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
543
        d_instantiate(dentry, inode);
Linus Torvalds's avatar
Linus Torvalds committed
544 545 546
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
547
int sysv_link(struct dentry * old_dentry, struct inode * dir, 
Linus Torvalds's avatar
Linus Torvalds committed
548
	      struct dentry * dentry)
Linus Torvalds's avatar
Linus Torvalds committed
549
{
Linus Torvalds's avatar
Linus Torvalds committed
550
	struct inode *oldinode = old_dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
551 552 553 554 555 556 557 558 559 560
	int error;
	struct sysv_dir_entry * de;
	struct buffer_head * bh;

	if (S_ISDIR(oldinode->i_mode)) {
		return -EPERM;
	}
	if (oldinode->i_nlink >= oldinode->i_sb->sv_link_max) {
		return -EMLINK;
	}
Linus Torvalds's avatar
Linus Torvalds committed
561 562
	bh = sysv_find_entry(dir, dentry->d_name.name,
                             dentry->d_name.len, &de);
Linus Torvalds's avatar
Linus Torvalds committed
563 564 565 566
	if (bh) {
		brelse(bh);
		return -EEXIST;
	}
Linus Torvalds's avatar
Linus Torvalds committed
567 568
	error = sysv_add_entry(dir, dentry->d_name.name,
                               dentry->d_name.len, &bh, &de);
Linus Torvalds's avatar
Linus Torvalds committed
569
	if (error) {
Linus Torvalds's avatar
Linus Torvalds committed
570
		brelse(bh);
Linus Torvalds's avatar
Linus Torvalds committed
571 572 573
		return error;
	}
	de->inode = oldinode->i_ino;
Linus Torvalds's avatar
Linus Torvalds committed
574
	mark_buffer_dirty(bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
575 576 577
	brelse(bh);
	oldinode->i_nlink++;
	oldinode->i_ctime = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
578
	mark_inode_dirty(oldinode);
Linus Torvalds's avatar
Linus Torvalds committed
579
	oldinode->i_count++;
Linus Torvalds's avatar
Linus Torvalds committed
580
        d_instantiate(dentry, oldinode);
Linus Torvalds's avatar
Linus Torvalds committed
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
	return 0;
}

#define PARENT_INO(buffer) \
(((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode)

/*
 * rename uses retrying to avoid race-conditions: at least they should be minimal.
 * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
 * checks fail, it tries to restart itself again. Very practical - no changes
 * are done until we know everything works ok.. and then all the changes can be
 * done in one fell swoop when we have claimed all the buffers needed.
 *
 * Anybody can rename anything with this: the permission checks are left to the
 * higher-level routines.
 */
Linus Torvalds's avatar
Linus Torvalds committed
597 598
static int do_sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
			  struct inode * new_dir, struct dentry * new_dentry)
Linus Torvalds's avatar
Linus Torvalds committed
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
{
	struct inode * old_inode, * new_inode;
	struct buffer_head * old_bh, * new_bh, * dir_bh;
	struct sysv_dir_entry * old_de, * new_de;
	int retval;

	goto start_up;
try_again:
	brelse(old_bh);
	brelse(new_bh);
	brelse(dir_bh);
	current->counter = 0;
	schedule();
start_up:
	old_inode = new_inode = NULL;
	old_bh = new_bh = dir_bh = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
615 616
	old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name,
				old_dentry->d_name.len, &old_de);
Linus Torvalds's avatar
Linus Torvalds committed
617 618 619
	retval = -ENOENT;
	if (!old_bh)
		goto end_rename;
Linus Torvalds's avatar
Linus Torvalds committed
620
	old_inode = old_dentry->d_inode;	/* don't cross mnt-points */
Linus Torvalds's avatar
Linus Torvalds committed
621
	retval = -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
622
	new_inode = new_dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
623 624
	new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name,
				new_dentry->d_name.len, &new_de);
Linus Torvalds's avatar
Linus Torvalds committed
625 626 627 628 629 630 631 632 633 634 635 636
	if (new_bh) {
		if (!new_inode) {
			brelse(new_bh);
			new_bh = NULL;
		}
	}
	if (new_inode == old_inode) {
		retval = 0;
		goto end_rename;
	}
	if (S_ISDIR(old_inode->i_mode)) {
		retval = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
637
		if (is_subdir(new_dentry, old_dentry))
Linus Torvalds's avatar
Linus Torvalds committed
638
			goto end_rename;
Linus Torvalds's avatar
Linus Torvalds committed
639 640 641 642 643 644 645 646 647 648
		if (new_inode) {
			if (new_dentry->d_count > 1)
				shrink_dcache_parent(new_dentry);
			retval = -EBUSY;
			if (new_dentry->d_count > 1)
				goto end_rename;
			retval = -ENOTEMPTY;
			if (!empty_dir(new_inode))
				goto end_rename;
		}
Linus Torvalds's avatar
Linus Torvalds committed
649
		retval = -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
650
		dir_bh = sysv_file_bread(old_inode, 0, 0);
Linus Torvalds's avatar
Linus Torvalds committed
651 652
		if (!dir_bh)
			goto end_rename;
Linus Torvalds's avatar
Linus Torvalds committed
653
		if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
Linus Torvalds's avatar
Linus Torvalds committed
654 655 656 657 658 659
			goto end_rename;
		retval = -EMLINK;
		if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
			goto end_rename;
	}
	if (!new_bh) {
Linus Torvalds's avatar
Linus Torvalds committed
660 661
		retval = sysv_add_entry(new_dir, new_dentry->d_name.name,
					new_dentry->d_name.len, &new_bh, &new_de);
Linus Torvalds's avatar
Linus Torvalds committed
662 663 664 665 666 667 668 669 670 671 672 673 674 675
		if (retval)
			goto end_rename;
	}
/* sanity checking before doing the rename - avoid races */
	if (new_inode && (new_de->inode != new_inode->i_ino))
		goto try_again;
	if (new_de->inode && !new_inode)
		goto try_again;
	if (old_de->inode != old_inode->i_ino)
		goto try_again;
/* ok, that's it */
	old_de->inode = 0;
	new_de->inode = old_inode->i_ino;
	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
676
	mark_inode_dirty(old_dir);
Linus Torvalds's avatar
Linus Torvalds committed
677
	new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
678
	mark_inode_dirty(new_dir);
Linus Torvalds's avatar
Linus Torvalds committed
679 680 681
	if (new_inode) {
		new_inode->i_nlink--;
		new_inode->i_ctime = CURRENT_TIME;
Linus Torvalds's avatar
Linus Torvalds committed
682
		mark_inode_dirty(new_inode);
Linus Torvalds's avatar
Linus Torvalds committed
683
	}
Linus Torvalds's avatar
Linus Torvalds committed
684 685
	mark_buffer_dirty(old_bh, 1);
	mark_buffer_dirty(new_bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
686
	if (dir_bh) {
Linus Torvalds's avatar
Linus Torvalds committed
687
		PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
Linus Torvalds's avatar
Linus Torvalds committed
688
		mark_buffer_dirty(dir_bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
689
		old_dir->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
690
		mark_inode_dirty(old_dir);
Linus Torvalds's avatar
Linus Torvalds committed
691 692
		if (new_inode) {
			new_inode->i_nlink--;
Linus Torvalds's avatar
Linus Torvalds committed
693
			mark_inode_dirty(new_inode);
Linus Torvalds's avatar
Linus Torvalds committed
694 695
		} else {
			new_dir->i_nlink++;
Linus Torvalds's avatar
Linus Torvalds committed
696
			mark_inode_dirty(new_dir);
Linus Torvalds's avatar
Linus Torvalds committed
697 698
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
699
	d_move(old_dentry, new_dentry);
Linus Torvalds's avatar
Linus Torvalds committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
	retval = 0;
end_rename:
	brelse(dir_bh);
	brelse(old_bh);
	brelse(new_bh);
	return retval;
}

/*
 * Ok, rename also locks out other renames, as they can change the parent of
 * a directory, and we don't want any races. Other races are checked for by
 * "do_rename()", which restarts if there are inconsistencies.
 *
 * Note that there is no race between different filesystems: it's only within
 * the same device that races occur: many renames can happen at once, as long
 * as they are on different partitions.
 */
Linus Torvalds's avatar
Linus Torvalds committed
717 718
int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
		struct inode * new_dir, struct dentry * new_dentry)
Linus Torvalds's avatar
Linus Torvalds committed
719 720 721 722 723 724 725 726
{
	static struct wait_queue * wait = NULL;
	static int lock = 0;
	int result;

	while (lock)
		sleep_on(&wait);
	lock = 1;
Linus Torvalds's avatar
Linus Torvalds committed
727 728
	result = do_sysv_rename(old_dir, old_dentry,
				new_dir, new_dentry);
Linus Torvalds's avatar
Linus Torvalds committed
729 730 731 732
	lock = 0;
	wake_up(&wait);
	return result;
}