link.c revision 31dbecd482405e0d3a67eb58e1a1c8cb9f2ad83e
1/*
2 * link.c --- create links in a ext2fs directory
3 *
4 * Copyright (C) 1993, 1994 Theodore Ts'o.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License.
9 * %End-Header%
10 */
11
12#include <stdio.h>
13#include <string.h>
14#if HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#if EXT2_FLAT_INCLUDES
19#include "ext2_fs.h"
20#else
21#include <linux/ext2_fs.h>
22#endif
23
24#include "ext2fs.h"
25
26struct link_struct  {
27	const char	*name;
28	int		namelen;
29	ext2_ino_t	inode;
30	int		flags;
31	int		done;
32	struct ext2_super_block *sb;
33};
34
35static int link_proc(struct ext2_dir_entry *dirent,
36		     int	offset,
37		     int	blocksize,
38		     char	*buf,
39		     void	*priv_data)
40{
41	struct link_struct *ls = (struct link_struct *) priv_data;
42	struct ext2_dir_entry *next;
43	int rec_len, min_rec_len;
44	int ret = 0;
45
46	rec_len = EXT2_DIR_REC_LEN(ls->namelen);
47
48	/*
49	 * See if the following directory entry (if any) is unused;
50	 * if so, absorb it into this one.
51	 */
52	next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
53	if ((offset + dirent->rec_len < blocksize - 8) &&
54	    (next->inode == 0) &&
55	    (offset + dirent->rec_len + next->rec_len <= blocksize)) {
56		dirent->rec_len += next->rec_len;
57		ret = DIRENT_CHANGED;
58	}
59
60	/*
61	 * If the directory entry is used, see if we can split the
62	 * directory entry to make room for the new name.  If so,
63	 * truncate it and return.
64	 */
65	if (dirent->inode) {
66		min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
67		if (dirent->rec_len < (min_rec_len + rec_len))
68			return ret;
69		rec_len = dirent->rec_len - min_rec_len;
70		dirent->rec_len = min_rec_len;
71		next = (struct ext2_dir_entry *) (buf + offset +
72						  dirent->rec_len);
73		next->inode = 0;
74		next->name_len = 0;
75		next->rec_len = rec_len;
76		return DIRENT_CHANGED;
77	}
78
79	/*
80	 * If we get this far, then the directory entry is not used.
81	 * See if we can fit the request entry in.  If so, do it.
82	 */
83	if (dirent->rec_len < rec_len)
84		return ret;
85	dirent->inode = ls->inode;
86	dirent->name_len = ls->namelen;
87	strncpy(dirent->name, ls->name, ls->namelen);
88	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
89		dirent->name_len |= (ls->flags & 0x7) << 8;
90
91	ls->done++;
92	return DIRENT_ABORT|DIRENT_CHANGED;
93}
94
95/*
96 * Note: the low 3 bits of the flags field are used as the directory
97 * entry filetype.
98 */
99#ifdef __TURBOC__
100 #pragma argsused
101#endif
102errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
103		      ext2_ino_t ino, int flags)
104{
105	errcode_t	retval;
106	struct link_struct ls;
107
108	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
109
110	if (!(fs->flags & EXT2_FLAG_RW))
111		return EXT2_ET_RO_FILSYS;
112
113	ls.name = name;
114	ls.namelen = name ? strlen(name) : 0;
115	ls.inode = ino;
116	ls.flags = flags;
117	ls.done = 0;
118	ls.sb = fs->super;
119
120	retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
121				    0, link_proc, &ls);
122	if (retval)
123		return retval;
124
125	return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
126}
127