tzh
2024-08-22 c7d0944258c7d0943aa7b2211498fd612971ce27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
 * link.c --- create links in a ext2fs directory
 *
 * Copyright (C) 1993, 1994 Theodore Ts'o.
 *
 * %Begin-Header%
 * This file may be redistributed under the terms of the GNU Library
 * General Public License, version 2.
 * %End-Header%
 */
 
#include "config.h"
#include <stdio.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
 
#include "ext2_fs.h"
#include "ext2fs.h"
 
struct link_struct  {
   ext2_filsys    fs;
   const char    *name;
   int        namelen;
   ext2_ino_t    inode;
   int        flags;
   int        done;
   unsigned int    blocksize;
   errcode_t    err;
   struct ext2_super_block *sb;
};
 
static int link_proc(struct ext2_dir_entry *dirent,
            int    offset,
            int    blocksize,
            char    *buf,
            void    *priv_data)
{
   struct link_struct *ls = (struct link_struct *) priv_data;
   struct ext2_dir_entry *next;
   unsigned int rec_len, min_rec_len, curr_rec_len;
   int ret = 0;
   int csum_size = 0;
   struct ext2_dir_entry_tail *t;
 
   if (ls->done)
       return DIRENT_ABORT;
 
   rec_len = EXT2_DIR_REC_LEN(ls->namelen);
 
   ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len);
   if (ls->err)
       return DIRENT_ABORT;
 
   if (ext2fs_has_feature_metadata_csum(ls->fs->super))
       csum_size = sizeof(struct ext2_dir_entry_tail);
   /*
    * See if the following directory entry (if any) is unused;
    * if so, absorb it into this one.
    */
   next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len);
   if ((offset + (int) curr_rec_len < blocksize - (8 + csum_size)) &&
       (next->inode == 0) &&
       (offset + (int) curr_rec_len + (int) next->rec_len <= blocksize)) {
       curr_rec_len += next->rec_len;
       ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
       if (ls->err)
           return DIRENT_ABORT;
       ret = DIRENT_CHANGED;
   }
 
   /*
    * Since ext2fs_link blows away htree data, we need to be
    * careful -- if metadata_csum is enabled and we're passed in
    * a dirent that contains htree data, we need to create the
    * fake entry at the end of the block that hides the checksum.
    */
 
   /* De-convert a dx_node block */
   if (csum_size &&
       curr_rec_len == ls->fs->blocksize &&
       !dirent->inode) {
       curr_rec_len -= csum_size;
       ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
       if (ls->err)
           return DIRENT_ABORT;
       t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
       ext2fs_initialize_dirent_tail(ls->fs, t);
       ret = DIRENT_CHANGED;
   }
 
   /* De-convert a dx_root block */
   if (csum_size &&
       curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) &&
       offset == EXT2_DIR_REC_LEN(1) &&
       dirent->name[0] == '.' && dirent->name[1] == '.') {
       curr_rec_len -= csum_size;
       ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
       if (ls->err)
           return DIRENT_ABORT;
       t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
       ext2fs_initialize_dirent_tail(ls->fs, t);
       ret = DIRENT_CHANGED;
   }
 
   /*
    * If the directory entry is used, see if we can split the
    * directory entry to make room for the new name.  If so,
    * truncate it and return.
    */
   if (dirent->inode) {
       min_rec_len = EXT2_DIR_REC_LEN(ext2fs_dirent_name_len(dirent));
       if (curr_rec_len < (min_rec_len + rec_len))
           return ret;
       rec_len = curr_rec_len - min_rec_len;
       ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent);
       if (ls->err)
           return DIRENT_ABORT;
       next = (struct ext2_dir_entry *) (buf + offset +
                         dirent->rec_len);
       next->inode = 0;
       ext2fs_dirent_set_name_len(next, 0);
       ext2fs_dirent_set_file_type(next, 0);
       ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next);
       if (ls->err)
           return DIRENT_ABORT;
       return DIRENT_CHANGED;
   }
 
   /*
    * If we get this far, then the directory entry is not used.
    * See if we can fit the request entry in.  If so, do it.
    */
   if (curr_rec_len < rec_len)
       return ret;
   dirent->inode = ls->inode;
   ext2fs_dirent_set_name_len(dirent, ls->namelen);
   strncpy(dirent->name, ls->name, ls->namelen);
   if (ext2fs_has_feature_filetype(ls->sb))
       ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7);
 
   ls->done++;
   return DIRENT_ABORT|DIRENT_CHANGED;
}
 
/*
 * Note: the low 3 bits of the flags field are used as the directory
 * entry filetype.
 */
#ifdef __TURBOC__
 #pragma argsused
#endif
errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
             ext2_ino_t ino, int flags)
{
   errcode_t        retval;
   struct link_struct    ls;
   struct ext2_inode    inode;
 
   EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
   if (!(fs->flags & EXT2_FLAG_RW))
       return EXT2_ET_RO_FILSYS;
 
   ls.fs = fs;
   ls.name = name;
   ls.namelen = name ? strlen(name) : 0;
   ls.inode = ino;
   ls.flags = flags;
   ls.done = 0;
   ls.sb = fs->super;
   ls.blocksize = fs->blocksize;
   ls.err = 0;
 
   retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
                   0, link_proc, &ls);
   if (retval)
       return retval;
   if (ls.err)
       return ls.err;
 
   if (!ls.done)
       return EXT2_ET_DIR_NO_SPACE;
 
   if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
       return retval;
 
   /*
    * If this function changes to preserve the htree, remove the
    * two hunks in link_proc that shove checksum tails into the
    * former dx_root/dx_node blocks.
    */
   if (inode.i_flags & EXT2_INDEX_FL) {
       inode.i_flags &= ~EXT2_INDEX_FL;
       if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
           return retval;
   }
 
   return 0;
}