Skip to content

Commit abf2ff7

Browse files
committed
Fix dentry of open files after rename
Signed-off-by: g2flyer <[email protected]>
1 parent 64cd864 commit abf2ff7

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

libos/src/sys/libos_file.c

+19
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,27 @@ static int do_rename(struct libos_dentry* old_dent, struct libos_dentry* new_den
355355

356356
if (new_dent->inode)
357357
put_inode(new_dent->inode);
358+
358359
new_dent->inode = old_dent->inode;
359360
old_dent->inode = NULL;
361+
// also update dentry of any potentially open fd pointing to old_dent
362+
struct libos_handle_map* handle_map = get_thread_handle_map(NULL);
363+
rwlock_write_lock(&handle_map->lock);
364+
365+
for (uint32_t i = 0; handle_map->fd_top != FD_NULL && i <= handle_map->fd_top; i++) {
366+
struct libos_fd_handle* fd_handle = handle_map->map[i];
367+
if (!HANDLE_ALLOCATED(fd_handle))
368+
continue;
369+
struct libos_handle* handle = fd_handle->handle;
370+
if ((handle->dentry != old_dent) || (handle->inode != new_dent->inode))
371+
continue;
372+
lock(&handle->lock);
373+
handle->dentry = new_dent;
374+
put_dentry(old_dent);
375+
get_dentry(new_dent);
376+
unlock(&handle->lock);
377+
}
378+
rwlock_write_unlock(&handle_map->lock);
360379
return 0;
361380
}
362381

libos/test/regression/rename_unlink.c

+91
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ static void test_rename_replace(const char* path1, const char* path2) {
156156
err(1, "rename");
157157

158158
should_not_exist(path1);
159+
159160
should_exist(path2, message1_len);
160161

161162
/* We expect `fd` to still point to old data, even though we replaced the file under its path */
@@ -177,6 +178,94 @@ static void test_rename_replace(const char* path1, const char* path2) {
177178
err(1, "unlink %s", path2);
178179
}
179180

181+
static void test_rename_follow(const char* path1, const char* path2) {
182+
printf("%s...\n", __func__);
183+
184+
int fd = create_file(path1, message1, message1_len);
185+
186+
if (fd < 0)
187+
err(1, "open %s", path1);
188+
189+
if (rename(path1, path2) != 0)
190+
err(1, "rename");
191+
192+
should_not_exist(path1);
193+
should_exist(path2, message1_len);
194+
195+
if (lseek(fd, 0, SEEK_SET) != 0)
196+
err(1, "lseek");
197+
198+
ssize_t n = posix_fd_write(fd, message2, message2_len);
199+
if (n < 0)
200+
errx(1, "posix_fd_write failed");
201+
if ((size_t)n != message2_len)
202+
errx(1, "wrote less bytes than expected");
203+
204+
should_contain("file opened before it's renamed", fd, message2, message2_len);
205+
206+
if (close(fd) != 0)
207+
err(1, "close %s", path2);
208+
209+
fd = open(path2, O_RDONLY, 0);
210+
if (fd < 0)
211+
err(1, "open %s", path2);
212+
213+
/* We expect `fd` to point to new data, even though we changed data via old fd after rename */
214+
should_contain("file opened after it's renamed", fd, message2, message2_len);
215+
216+
if (close(fd) != 0)
217+
err(1, "close %s", path2);
218+
219+
if (unlink(path2) != 0)
220+
err(1, "unlink %s", path2);
221+
}
222+
223+
// NOTE: below will _not_ run correctly when directly executed unless you run as root.
224+
// But it should run properly in gramine when executed as normal user.
225+
static void test_rename_fchown_fchmod(const char* path1, const char* path2) {
226+
printf("%s...\n", __func__);
227+
228+
int fd = create_file(path1, message1, message1_len);
229+
230+
if (fchown(fd, 1, 1))
231+
err(1, "fchown before rename");
232+
if (fchmod(fd, S_IRWXU | S_IRWXG) != 0) // Note: no other!
233+
err(1, "fchmod before rename");
234+
struct stat st;
235+
if (stat(path1, &st) != 0)
236+
err(1, "Failed to stat file %s", path1);
237+
if (st.st_uid != 1 || st.st_gid != 1)
238+
err(1, "wrong ownership of file %s", path1);
239+
if (st.st_mode & S_IRWXO)
240+
err(1, "wrong permissions of file %s", path1);
241+
242+
if (fd < 0)
243+
err(1, "open %s", path1);
244+
245+
if (rename(path1, path2) != 0)
246+
err(1, "rename");
247+
248+
should_not_exist(path1);
249+
should_exist(path2, message1_len);
250+
251+
if (fchown(fd, 2, 2))
252+
err(1, "fchown after rename");
253+
if (fchmod(fd, S_IRWXU | S_IRWXG | S_IRWXO) != 0) // Note: with other now!
254+
err(1, "fchmod after rename");
255+
if (stat(path2, &st) != 0)
256+
err(1, "Failed to stat (renamed) file %s", path2);
257+
if (st.st_uid != 2 || st.st_gid != 2)
258+
err(1, "wrong ownership of (renamed) file %s", path2);
259+
if (!(st.st_mode & S_IRWXO))
260+
err(1, "wrong permissions of (renamed) file %s", path2);
261+
262+
if (close(fd) != 0)
263+
err(1, "close %s", path2);
264+
265+
if (unlink(path2) != 0)
266+
err(1, "unlink %s", path2);
267+
}
268+
180269
static void test_rename_open_file(const char* path1, const char* path2) {
181270
printf("%s...\n", __func__);
182271

@@ -271,6 +360,8 @@ int main(int argc, char* argv[]) {
271360
test_rename_same_file(path1);
272361
test_simple_rename(path1, path2);
273362
test_rename_replace(path1, path2);
363+
test_rename_follow(path1, path2);
364+
test_rename_fchown_fchmod(path1, path2);
274365
test_rename_open_file(path1, path2);
275366
test_unlink_and_recreate(path1);
276367
test_unlink_and_write(path1);

0 commit comments

Comments
 (0)