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