blob: 08b2e9673846eb2610eed0228b793c54d2d2d55b
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 | } |
136 |