Skip to content

Commit bd817ab

Browse files
committed
Added support for renaming dirs/files
1 parent 3b1bcbe commit bd817ab

File tree

3 files changed

+278
-2
lines changed

3 files changed

+278
-2
lines changed

lfs.c

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
13371337
cwd.d.size -= entry.d.len;
13381338

13391339
// either shift out the one entry or remove the whole dir block
1340-
if (cwd.d.size == sizeof(dir.d)) {
1340+
if (cwd.d.size == sizeof(cwd.d)) {
13411341
lfs_dir_t pdir;
13421342
int err = lfs_dir_fetch(lfs, &pdir, lfs->cwd);
13431343
if (err) {
@@ -1418,6 +1418,179 @@ int lfs_remove(lfs_t *lfs, const char *path) {
14181418
return 0;
14191419
}
14201420

1421+
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
1422+
// Find old entry
1423+
lfs_dir_t oldcwd;
1424+
int err = lfs_dir_fetch(lfs, &oldcwd, lfs->cwd);
1425+
if (err) {
1426+
return err;
1427+
}
1428+
1429+
lfs_entry_t oldentry;
1430+
err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldentry);
1431+
if (err) {
1432+
return err;
1433+
}
1434+
1435+
// Allocate new entry
1436+
lfs_dir_t newcwd;
1437+
err = lfs_dir_fetch(lfs, &newcwd, lfs->cwd);
1438+
if (err) {
1439+
return err;
1440+
}
1441+
1442+
lfs_entry_t preventry;
1443+
err = lfs_dir_append(lfs, &newcwd, &newpath, &preventry);
1444+
if (err && err != LFS_ERROR_EXISTS) {
1445+
return err;
1446+
}
1447+
bool prevexists = (err == LFS_ERROR_EXISTS);
1448+
1449+
// must have same type
1450+
if (prevexists && preventry.d.type != oldentry.d.type) {
1451+
return LFS_ERROR_INVALID;
1452+
}
1453+
1454+
lfs_dir_t dir;
1455+
if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
1456+
// must be empty before removal, checking size
1457+
// without masking top bit checks for any case where
1458+
// dir is not empty
1459+
int err = lfs_dir_fetch(lfs, &dir, preventry.d.u.dir);
1460+
if (err) {
1461+
return err;
1462+
} else if (dir.d.size != sizeof(dir.d)) {
1463+
return LFS_ERROR_INVALID;
1464+
}
1465+
}
1466+
1467+
// Move to new location
1468+
lfs_entry_t newentry = preventry;
1469+
newentry.d = oldentry.d;
1470+
newentry.d.len = sizeof(newentry.d) + strlen(newpath);
1471+
1472+
newcwd.d.rev += 1;
1473+
if (!prevexists) {
1474+
newcwd.d.size += newentry.d.len;
1475+
}
1476+
1477+
err = lfs_pair_commit(lfs, newentry.dir,
1478+
3, (struct lfs_commit_region[3]) {
1479+
{0, sizeof(newcwd.d), &newcwd.d},
1480+
{newentry.off,
1481+
sizeof(newentry.d),
1482+
&newentry.d},
1483+
{newentry.off+sizeof(newentry.d),
1484+
newentry.d.len - sizeof(newentry.d),
1485+
newpath}
1486+
});
1487+
if (err) {
1488+
return err;
1489+
}
1490+
1491+
// fetch again in case newcwd == oldcwd
1492+
// TODO handle this better?
1493+
err = lfs_dir_fetch(lfs, &oldcwd, oldcwd.pair);
1494+
if (err) {
1495+
return err;
1496+
}
1497+
1498+
err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldentry);
1499+
if (err) {
1500+
return err;
1501+
}
1502+
1503+
// Remove from old location
1504+
// TODO abstract this out for rename + remove?
1505+
oldcwd.d.rev += 1;
1506+
oldcwd.d.size -= oldentry.d.len;
1507+
1508+
// either shift out the one entry or remove the whole dir block
1509+
if (oldcwd.d.size == sizeof(oldcwd.d)) {
1510+
lfs_dir_t pdir;
1511+
int err = lfs_dir_fetch(lfs, &pdir, lfs->cwd);
1512+
if (err) {
1513+
return err;
1514+
}
1515+
1516+
while (lfs_paircmp(pdir.d.tail, oldcwd.pair) != 0) {
1517+
int err = lfs_dir_fetch(lfs, &pdir, pdir.d.tail);
1518+
if (err) {
1519+
return err;
1520+
}
1521+
}
1522+
1523+
// TODO easier check for head block? (common case)
1524+
if (!(pdir.d.size & 0x80000000)) {
1525+
int err = lfs_pair_shift(lfs, oldentry.dir,
1526+
1, (struct lfs_commit_region[]) {
1527+
{0, sizeof(oldcwd.d), &oldcwd.d},
1528+
},
1529+
oldentry.off, oldentry.d.len);
1530+
if (err) {
1531+
return err;
1532+
}
1533+
} else {
1534+
pdir.d.tail[0] = oldcwd.d.tail[0];
1535+
pdir.d.tail[1] = oldcwd.d.tail[1];
1536+
pdir.d.rev += 1;
1537+
1538+
err = lfs_pair_commit(lfs, pdir.pair,
1539+
1, (struct lfs_commit_region[]) {
1540+
{0, sizeof(pdir.d), &pdir.d},
1541+
});
1542+
if (err) {
1543+
return err;
1544+
}
1545+
}
1546+
} else {
1547+
int err = lfs_pair_shift(lfs, oldentry.dir,
1548+
1, (struct lfs_commit_region[]) {
1549+
{0, sizeof(oldcwd.d), &oldcwd.d},
1550+
},
1551+
oldentry.off, oldentry.d.len);
1552+
if (err) {
1553+
return err;
1554+
}
1555+
}
1556+
1557+
// TODO abstract this out for rename + remove?
1558+
if (prevexists && preventry.d.type == LFS_TYPE_DIR) {
1559+
// remove dest from the dir list
1560+
// this may create an orphan, which must be deorphaned
1561+
lfs_dir_t pdir;
1562+
int err = lfs_dir_fetch(lfs, &pdir, lfs->root);
1563+
if (err) {
1564+
return err;
1565+
}
1566+
1567+
while (pdir.d.tail[0]) {
1568+
if (lfs_paircmp(pdir.d.tail, preventry.d.u.dir) == 0) {
1569+
pdir.d.tail[0] = dir.d.tail[0];
1570+
pdir.d.tail[1] = dir.d.tail[1];
1571+
pdir.d.rev += 1;
1572+
1573+
int err = lfs_pair_commit(lfs, pdir.pair,
1574+
1, (struct lfs_commit_region[]) {
1575+
{0, sizeof(pdir.d), &pdir.d},
1576+
});
1577+
if (err) {
1578+
return err;
1579+
}
1580+
1581+
break;
1582+
}
1583+
1584+
int err = lfs_dir_fetch(lfs, &pdir, pdir.d.tail);
1585+
if (err) {
1586+
return err;
1587+
}
1588+
}
1589+
}
1590+
1591+
return 0;
1592+
}
1593+
14211594
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
14221595
lfs_dir_t cwd;
14231596
int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd);

lfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
138138
int lfs_unmount(lfs_t *lfs);
139139

140140
int lfs_remove(lfs_t *lfs, const char *path);
141+
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
141142
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
142143

143144
int lfs_mkdir(lfs_t *lfs, const char *path);

tests/test_dirs.sh

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ tests/test.py << TEST
123123
lfs_unmount(&lfs) => 0;
124124
TEST
125125

126-
echo "--- Directory deletion ---"
126+
echo "--- Directory remove ---"
127127
tests/test.py << TEST
128128
lfs_mount(&lfs, &config) => 0;
129129
lfs_remove(&lfs, "potato") => LFS_ERROR_INVALID;
@@ -180,5 +180,107 @@ tests/test.py << TEST
180180
lfs_unmount(&lfs) => 0;
181181
TEST
182182

183+
echo "--- Directory rename ---"
184+
tests/test.py << TEST
185+
lfs_mount(&lfs, &config) => 0;
186+
lfs_mkdir(&lfs, "coldpotato") => 0;
187+
lfs_mkdir(&lfs, "coldpotato/baked") => 0;
188+
lfs_mkdir(&lfs, "coldpotato/sweet") => 0;
189+
lfs_mkdir(&lfs, "coldpotato/fried") => 0;
190+
lfs_unmount(&lfs) => 0;
191+
TEST
192+
tests/test.py << TEST
193+
lfs_mount(&lfs, &config) => 0;
194+
lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
195+
lfs_unmount(&lfs) => 0;
196+
TEST
197+
tests/test.py << TEST
198+
lfs_mount(&lfs, &config) => 0;
199+
lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0;
200+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
201+
strcmp(info.name, ".") => 0;
202+
info.type => LFS_TYPE_DIR;
203+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
204+
strcmp(info.name, "..") => 0;
205+
info.type => LFS_TYPE_DIR;
206+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
207+
strcmp(info.name, "baked") => 0;
208+
info.type => LFS_TYPE_DIR;
209+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
210+
strcmp(info.name, "sweet") => 0;
211+
info.type => LFS_TYPE_DIR;
212+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
213+
strcmp(info.name, "fried") => 0;
214+
info.type => LFS_TYPE_DIR;
215+
lfs_dir_read(&lfs, &dir[0], &info) => 0;
216+
lfs_dir_close(&lfs, &dir[0]) => 0;
217+
lfs_unmount(&lfs) => 0;
218+
TEST
219+
tests/test.py << TEST
220+
lfs_mount(&lfs, &config) => 0;
221+
lfs_mkdir(&lfs, "warmpotato") => 0;
222+
lfs_mkdir(&lfs, "warmpotato/mushy") => 0;
223+
lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERROR_INVALID;
224+
225+
lfs_remove(&lfs, "warmpotato/mushy") => 0;
226+
lfs_rename(&lfs, "hotpotato", "warmpotato") => 0;
227+
228+
lfs_unmount(&lfs) => 0;
229+
TEST
230+
tests/test.py << TEST
231+
lfs_mount(&lfs, &config) => 0;
232+
lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0;
233+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
234+
strcmp(info.name, ".") => 0;
235+
info.type => LFS_TYPE_DIR;
236+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
237+
strcmp(info.name, "..") => 0;
238+
info.type => LFS_TYPE_DIR;
239+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
240+
strcmp(info.name, "baked") => 0;
241+
info.type => LFS_TYPE_DIR;
242+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
243+
strcmp(info.name, "sweet") => 0;
244+
info.type => LFS_TYPE_DIR;
245+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
246+
strcmp(info.name, "fried") => 0;
247+
info.type => LFS_TYPE_DIR;
248+
lfs_dir_read(&lfs, &dir[0], &info) => 0;
249+
lfs_dir_close(&lfs, &dir[0]) => 0;
250+
lfs_unmount(&lfs) => 0;
251+
TEST
252+
tests/test.py << TEST
253+
lfs_mount(&lfs, &config) => 0;
254+
lfs_mkdir(&lfs, "coldpotato") => 0;
255+
lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0;
256+
lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0;
257+
lfs_rename(&lfs, "warmpotato/fried", "coldpotato/fried") => 0;
258+
lfs_remove(&lfs, "coldpotato") => LFS_ERROR_INVALID;
259+
lfs_remove(&lfs, "warmpotato") => 0;
260+
lfs_unmount(&lfs) => 0;
261+
TEST
262+
tests/test.py << TEST
263+
lfs_mount(&lfs, &config) => 0;
264+
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
265+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
266+
strcmp(info.name, ".") => 0;
267+
info.type => LFS_TYPE_DIR;
268+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
269+
strcmp(info.name, "..") => 0;
270+
info.type => LFS_TYPE_DIR;
271+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
272+
strcmp(info.name, "baked") => 0;
273+
info.type => LFS_TYPE_DIR;
274+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
275+
strcmp(info.name, "sweet") => 0;
276+
info.type => LFS_TYPE_DIR;
277+
lfs_dir_read(&lfs, &dir[0], &info) => 1;
278+
strcmp(info.name, "fried") => 0;
279+
info.type => LFS_TYPE_DIR;
280+
lfs_dir_read(&lfs, &dir[0], &info) => 0;
281+
lfs_dir_close(&lfs, &dir[0]) => 0;
282+
lfs_unmount(&lfs) => 0;
283+
TEST
284+
183285
echo "--- Results ---"
184286
tests/stats.py

0 commit comments

Comments
 (0)