]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - lib/ext2fs/link.c
Many files:
[thirdparty/e2fsprogs.git] / lib / ext2fs / link.c
1 /*
2 * link.c --- create or delete links in a ext2fs directory
3 *
4 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <linux/fs.h>
13 #include <linux/ext2_fs.h>
14
15 #include "ext2fs.h"
16
17 struct link_struct {
18 const char *name;
19 int namelen;
20 ino_t inode;
21 int flags;
22 int done;
23 };
24
25 static int link_proc(struct ext2_dir_entry *dirent,
26 int offset,
27 int blocksize,
28 char *buf,
29 void *private)
30 {
31 struct link_struct *ls = (struct link_struct *) private;
32 struct ext2_dir_entry *next;
33 int rec_len;
34 int ret = 0;
35
36 rec_len = EXT2_DIR_REC_LEN(ls->namelen);
37
38 /*
39 * See if the following directory entry (if any) is unused;
40 * if so, absorb it into this one.
41 */
42 next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
43 if ((offset + dirent->rec_len < blocksize - 8) &&
44 (next->inode == 0) &&
45 (offset + dirent->rec_len + next->rec_len <= blocksize)) {
46 dirent->rec_len += next->rec_len;
47 ret = DIRENT_CHANGED;
48 }
49
50 /*
51 * If the directory entry is used, see if we can split the
52 * directory entry to make room for the new name. If so,
53 * truncate it and return.
54 */
55 if (dirent->inode) {
56 if (dirent->rec_len < (EXT2_DIR_REC_LEN(dirent->name_len) +
57 rec_len))
58 return ret;
59 rec_len = dirent->rec_len - EXT2_DIR_REC_LEN(dirent->name_len);
60 dirent->rec_len = EXT2_DIR_REC_LEN(dirent->name_len);
61 next = (struct ext2_dir_entry *) (buf + offset +
62 dirent->rec_len);
63 next->inode = 0;
64 next->name_len = 0;
65 next->rec_len = rec_len;
66 return DIRENT_CHANGED;
67 }
68
69 /*
70 * If we get this far, then the directory entry is not used.
71 * See if we can fit the request entry in. If so, do it.
72 */
73 if (dirent->rec_len < rec_len)
74 return ret;
75 dirent->inode = ls->inode;
76 dirent->name_len = ls->namelen;
77 strncpy(dirent->name, ls->name, ls->namelen);
78
79 ls->done++;
80 return DIRENT_ABORT|DIRENT_CHANGED;
81 }
82
83 errcode_t ext2fs_link(ext2_filsys fs, ino_t dir, const char *name, ino_t ino,
84 int flags)
85 {
86 errcode_t retval;
87 struct link_struct ls;
88
89 if (!(fs->flags & EXT2_FLAG_RW))
90 return EXT2_ET_RO_FILSYS;
91
92 ls.name = name;
93 ls.namelen = name ? strlen(name) : 0;
94 ls.inode = ino;
95 ls.flags = 0;
96 ls.done = 0;
97
98 retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
99 0, link_proc, &ls);
100 if (retval)
101 return retval;
102
103 return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
104 }
105
106 static int unlink_proc(struct ext2_dir_entry *dirent,
107 int offset,
108 int blocksize,
109 char *buf,
110 void *private)
111 {
112 struct link_struct *ls = (struct link_struct *) private;
113
114 if (ls->name && (dirent->name_len != ls->namelen))
115 return 0;
116 if (ls->name && strncmp(ls->name, dirent->name, dirent->name_len))
117 return 0;
118 if (ls->inode && (dirent->inode != ls->inode))
119 return 0;
120
121 dirent->inode = 0;
122 ls->done++;
123 return DIRENT_ABORT|DIRENT_CHANGED;
124 }
125
126 errcode_t ext2fs_unlink(ext2_filsys fs, ino_t dir, const char *name, ino_t ino,
127 int flags)
128 {
129 errcode_t retval;
130 struct link_struct ls;
131
132 if (!(fs->flags & EXT2_FLAG_RW))
133 return EXT2_ET_RO_FILSYS;
134
135 ls.name = name;
136 ls.namelen = name ? strlen(name) : 0;
137 ls.inode = ino;
138 ls.flags = 0;
139 ls.done = 0;
140
141 retval = ext2fs_dir_iterate(fs, dir, 0, 0, unlink_proc, &ls);
142 if (retval)
143 return retval;
144
145 return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
146 }
147