Skip to content

Commit b9dd32a

Browse files
committed
fix some minor quota issues, clean up code a bit
1 parent 9d1590c commit b9dd32a

3 files changed

Lines changed: 258 additions & 43 deletions

File tree

src/operations.cpp

Lines changed: 206 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ static void enforce_ownership(int dirfd, const char *name, int fd = -1) {
5757
}
5858

5959
if (res == -1) {
60-
error_print("WARNING: Failed to chown created file '{}': {}", name, errno);
60+
if (name)
61+
error_print("WARNING: Failed to chown created file '{}': {}", name, errno);
62+
else
63+
error_print("WARNING: Failed to fchown fd {}: {}", fd, errno);
6164
}
6265
}
6366

@@ -74,8 +77,9 @@ static void forget_one(fuse_ino_t ino, uint64_t n) {
7477
debug_print("forget: inode {} count now {}", inode.src_ino, inode.nlookup.load());
7578

7679
if (!inode.nlookup) {
77-
std::lock_guard<std::mutex> g_fs{fs.mutex};
7880
l.unlock();
81+
std::lock_guard<std::mutex> g_fs{fs.mutex};
82+
7983
if (!inode.nlookup) {
8084
debug_print("forget: cleaning up inode {}", inode.src_ino);
8185
fs.inodes.erase({inode.src_ino, inode.src_dev});
@@ -192,6 +196,26 @@ static void sfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int v
192196
int ifd = inode.fd;
193197
int res = 0;
194198

199+
if (valid & FUSE_SET_ATTR_MODE) {
200+
res = with_fd_path(ifd,
201+
[attr](const char *procname) { return chmod(procname, attr->st_mode); });
202+
if (res == -1) {
203+
fuse_reply_err(req, errno);
204+
return;
205+
}
206+
}
207+
208+
if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
209+
uid_t u = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : static_cast<uid_t>(-1);
210+
gid_t g = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : static_cast<gid_t>(-1);
211+
212+
res = with_fd_path(ifd, [u, g](const char *procname) { return lchown(procname, u, g); });
213+
if (res == -1) {
214+
fuse_reply_err(req, errno);
215+
return;
216+
}
217+
}
218+
195219
if (valid & FUSE_SET_ATTR_SIZE) {
196220
std::unique_lock<std::mutex> g{inode.m};
197221

@@ -228,6 +252,43 @@ static void sfs_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int v
228252
g.unlock();
229253
}
230254

255+
if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
256+
struct timespec tv[2];
257+
258+
tv[0].tv_sec = 0;
259+
tv[0].tv_nsec = UTIME_OMIT;
260+
tv[1].tv_sec = 0;
261+
tv[1].tv_nsec = UTIME_OMIT;
262+
263+
if (valid & FUSE_SET_ATTR_ATIME) {
264+
if (valid & FUSE_SET_ATTR_ATIME_NOW) {
265+
tv[0].tv_nsec = UTIME_NOW;
266+
} else {
267+
tv[0] = attr->st_atim;
268+
}
269+
}
270+
271+
if (valid & FUSE_SET_ATTR_MTIME) {
272+
if (valid & FUSE_SET_ATTR_MTIME_NOW) {
273+
tv[1].tv_nsec = UTIME_NOW;
274+
} else {
275+
tv[1] = attr->st_mtim;
276+
}
277+
}
278+
279+
if (fi) {
280+
res = futimens(fi->fh, tv);
281+
} else {
282+
std::string procname = std::format("/proc/self/fd/{}", ifd);
283+
res = utimensat(AT_FDCWD, procname.c_str(), tv, 0);
284+
}
285+
286+
if (res == -1) {
287+
fuse_reply_err(req, errno);
288+
return;
289+
}
290+
}
291+
231292
return sfs_getattr(req, ino, fi);
232293
}
233294

@@ -250,9 +311,29 @@ static void sfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) {
250311

251312
static void mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode,
252313
dev_t rdev, const char *link) {
253-
int res;
254314
Inode &inode_p = get_inode(parent);
255315

316+
if (fs.quota.is_enabled()) {
317+
if (S_ISDIR(mode)) {
318+
if (!fs.quota.reserve(0, fs.blocksize)) {
319+
fuse_reply_err(req, ENOSPC);
320+
return;
321+
}
322+
} else if (S_ISLNK(mode) && link) {
323+
uint64_t link_size = strlen(link);
324+
if (!fs.quota.reserve(0, link_size)) {
325+
fuse_reply_err(req, ENOSPC);
326+
return;
327+
}
328+
} else if (S_ISREG(mode)) {
329+
if (!fs.quota.check_available(0)) {
330+
fuse_reply_err(req, ENOSPC);
331+
return;
332+
}
333+
}
334+
}
335+
336+
int res;
256337
if (S_ISDIR(mode))
257338
res = mkdirat(inode_p.fd, name, mode);
258339
else if (S_ISLNK(mode))
@@ -262,6 +343,14 @@ static void mknod_symlink(fuse_req_t req, fuse_ino_t parent, const char *name, m
262343

263344
int saverr = errno;
264345
if (res == -1) {
346+
if (fs.quota.is_enabled()) {
347+
if (S_ISDIR(mode)) {
348+
fs.quota.release(fs.blocksize, 0);
349+
} else if (S_ISLNK(mode) && link) {
350+
fs.quota.release(strlen(link), 0);
351+
}
352+
}
353+
265354
if (saverr == ENFILE || saverr == EMFILE)
266355
error_print("Reached maximum number of file descriptors.");
267356
fuse_reply_err(req, saverr);
@@ -319,9 +408,24 @@ static void sfs_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, const ch
319408

320409
static void sfs_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) {
321410
Inode &inode_p = get_inode(parent);
411+
412+
uint64_t dir_size = 0;
413+
if (fs.quota.is_enabled()) {
414+
struct stat st;
415+
if (fstatat(inode_p.fd, name, &st, AT_SYMLINK_NOFOLLOW) == 0) {
416+
dir_size = st.st_size;
417+
}
418+
}
419+
322420
std::lock_guard<std::mutex> g{inode_p.m};
323421
auto res = unlinkat(inode_p.fd, name, AT_REMOVEDIR);
324-
fuse_reply_err(req, res == -1 ? errno : 0);
422+
if (res == -1) {
423+
fuse_reply_err(req, errno);
424+
} else {
425+
if (dir_size > 0)
426+
fs.quota.release(dir_size, 0);
427+
fuse_reply_err(req, 0);
428+
}
325429
}
326430

327431
static void sfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent,
@@ -332,8 +436,25 @@ static void sfs_rename(fuse_req_t req, fuse_ino_t parent, const char *name, fuse
332436
fuse_reply_err(req, EINVAL);
333437
return;
334438
}
439+
440+
uint64_t victim_size = 0;
441+
nlink_t victim_nlink = 0;
442+
if (fs.quota.is_enabled()) {
443+
struct stat st;
444+
if (fstatat(inode_np.fd, newname, &st, AT_SYMLINK_NOFOLLOW) == 0) {
445+
victim_size = st.st_size;
446+
victim_nlink = st.st_nlink;
447+
}
448+
}
449+
335450
auto res = renameat(inode_p.fd, name, inode_np.fd, newname);
336-
fuse_reply_err(req, res == -1 ? errno : 0);
451+
if (res == -1) {
452+
fuse_reply_err(req, errno);
453+
} else {
454+
if (victim_size > 0 && victim_nlink == 1)
455+
fs.quota.release(victim_size, 0);
456+
fuse_reply_err(req, 0);
457+
}
337458
}
338459

339460
static void sfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
@@ -346,9 +467,22 @@ static void sfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
346467
return;
347468
}
348469

470+
auto res = unlinkat(inode_p.fd, name, 0);
471+
if (res == -1) {
472+
forget_one(e.ino, 1);
473+
fuse_reply_err(req, errno);
474+
return;
475+
}
476+
349477
if (fs.quota.is_enabled() && e.attr.st_nlink == 1) {
350-
fs.quota.release(e.attr.st_size, 0);
351-
debug_print("QUOTA: Reclaimed {} bytes from unlink", e.attr.st_size);
478+
Inode &inode = get_inode(e.ino);
479+
std::lock_guard<std::mutex> g{inode.m};
480+
uint64_t size = inode.known_size;
481+
if (S_ISLNK(e.attr.st_mode)) {
482+
size = e.attr.st_size;
483+
}
484+
fs.quota.release(size, 0);
485+
debug_print("QUOTA: Reclaimed {} bytes from unlink", size);
352486
}
353487

354488
if (!fs.timeout) {
@@ -366,8 +500,7 @@ static void sfs_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) {
366500

367501
forget_one(e.ino, 1);
368502

369-
auto res = unlinkat(inode_p.fd, name, 0);
370-
fuse_reply_err(req, res == -1 ? errno : 0);
503+
fuse_reply_err(req, 0);
371504
}
372505

373506
static void sfs_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) {
@@ -561,18 +694,42 @@ static void sfs_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode
561694
debug_print("sfs_create: parent ino {}, name '{}', mode {:o}, flags {:o}", parent, name, mode,
562695
fi->flags);
563696

697+
uint64_t old_file_size = 0;
698+
bool truncating_existing = false;
699+
700+
if (fs.quota.is_enabled()) {
701+
if (fi->flags & O_TRUNC) {
702+
struct stat st;
703+
if (fstatat(inode_p.fd, name, &st, AT_SYMLINK_NOFOLLOW) == 0 && S_ISREG(st.st_mode)) {
704+
old_file_size = st.st_size;
705+
truncating_existing = true;
706+
}
707+
}
708+
709+
if (!truncating_existing && !fs.quota.check_available(0)) {
710+
fuse_reply_err(req, ENOSPC);
711+
return;
712+
}
713+
}
714+
564715
int fd = openat(inode_p.fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode);
565716
if (fd == -1) {
566717
fuse_reply_err(req, errno);
567718
return;
568719
}
569720

721+
if (truncating_existing && old_file_size > 0) {
722+
fs.quota.release(old_file_size, 0);
723+
debug_print("QUOTA: Released {} bytes from O_TRUNC in create", old_file_size);
724+
}
725+
570726
enforce_ownership(inode_p.fd, name, fd);
571727

572728
fi->fh = fd;
573729
fuse_entry_param e;
574730
auto err = do_lookup(parent, name, &e);
575731
if (err) {
732+
close(fd);
576733
fuse_reply_err(req, err);
577734
return;
578735
}
@@ -581,6 +738,10 @@ static void sfs_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode
581738
std::lock_guard<std::mutex> g{inode.m};
582739
inode.nopen++;
583740

741+
if (truncating_existing) {
742+
inode.known_size = 0;
743+
}
744+
584745
sfs_create_open_flags(fi);
585746

586747
fuse_reply_create(req, &e, fi);
@@ -589,6 +750,12 @@ static void sfs_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode
589750
static void sfs_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
590751
Inode &inode = get_inode(ino);
591752

753+
uint64_t trunc_release = 0;
754+
if (fs.quota.is_enabled() && (fi->flags & O_TRUNC)) {
755+
std::lock_guard<std::mutex> g{inode.m};
756+
trunc_release = inode.known_size;
757+
}
758+
592759
if (fs.timeout && (fi->flags & O_ACCMODE) == O_WRONLY) {
593760
fi->flags &= ~O_ACCMODE;
594761
fi->flags |= O_RDWR;
@@ -604,10 +771,19 @@ static void sfs_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi) {
604771
return;
605772
}
606773

774+
if (trunc_release > 0) {
775+
fs.quota.release(trunc_release, 0);
776+
debug_print("QUOTA: Released {} bytes from O_TRUNC in open", trunc_release);
777+
}
778+
607779
enforce_ownership(-1, nullptr, fd);
608780

609781
std::lock_guard<std::mutex> g{inode.m};
610782
inode.nopen++;
783+
784+
if (trunc_release > 0)
785+
inode.known_size = 0;
786+
611787
sfs_create_open_flags(fi);
612788
fi->fh = fd;
613789

@@ -702,10 +878,26 @@ static void sfs_write_buf(fuse_req_t req, fuse_ino_t ino, fuse_bufvec *in_buf, o
702878

703879
if (res < 0) {
704880
if (reserved) {
881+
std::lock_guard<std::mutex> g{inode.m};
882+
inode.known_size = pre_write_size;
705883
fs.quota.release(pre_write_size + amount_reserved, pre_write_size);
706884
}
707885
fuse_reply_err(req, -res);
708886
} else {
887+
if (reserved) {
888+
uint64_t actual_end = off + static_cast<uint64_t>(res);
889+
if (actual_end < write_end) {
890+
std::lock_guard<std::mutex> g{inode.m};
891+
892+
if (inode.known_size == write_end) {
893+
uint64_t corrected = std::max(pre_write_size, actual_end);
894+
if (corrected < write_end) {
895+
fs.quota.release(write_end, corrected);
896+
inode.known_size = corrected;
897+
}
898+
}
899+
}
900+
}
709901
fuse_reply_write(req, (size_t)res);
710902
}
711903
}
@@ -760,8 +952,12 @@ static void sfs_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset
760952
err = errno;
761953

762954
if (err != 0) {
763-
if (reserved)
955+
if (reserved) {
956+
std::lock_guard<std::mutex> g2{inode.m};
957+
if (inode.known_size == end)
958+
inode.known_size = current;
764959
fs.quota.release(end, current);
960+
}
765961
fuse_reply_err(req, err);
766962
} else {
767963
fuse_reply_err(req, 0);
@@ -783,8 +979,6 @@ static void sfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_
783979
ssize_t ret = getxattr(procname.c_str(), name, value.data(), size);
784980
if (ret == -1)
785981
fuse_reply_err(req, errno);
786-
else if (ret == 0)
787-
fuse_reply_err(req, 0);
788982
else
789983
fuse_reply_buf(req, value.data(), ret);
790984
} else {
@@ -805,8 +999,6 @@ static void sfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) {
805999
ssize_t ret = listxattr(procname.c_str(), value.data(), size);
8061000
if (ret == -1)
8071001
fuse_reply_err(req, errno);
808-
else if (ret == 0)
809-
fuse_reply_err(req, 0);
8101002
else
8111003
fuse_reply_buf(req, value.data(), ret);
8121004
} else {

0 commit comments

Comments
 (0)