Summary: | zfs: quotactl() returns wrong error | ||||||||
---|---|---|---|---|---|---|---|---|---|
Product: | Base System | Reporter: | Emrion <kmachine> | ||||||
Component: | misc | Assignee: | Sean Eric Fagan <sef> | ||||||
Status: | Closed FIXED | ||||||||
Severity: | Affects Some People | CC: | driesm, freebsd.bugs, kmachine, lapo, markj, sef, sigsys, timur, topical | ||||||
Priority: | --- | Keywords: | regression | ||||||
Version: | 12.0-RELEASE | Flags: | koobs:
mfc-stable12+
koobs: mfc-stable11+ |
||||||
Hardware: | Any | ||||||||
OS: | Any | ||||||||
Attachments: |
|
Description
Emrion
2018-12-26 12:02:26 UTC
This issue is visible when samba operates on a zfs file system but I think it has issued more problems. When a call to quotactl() is made on a zfs dataset, the error generated under 11.2 FreeBSD is: Operation not supported (ENOTSUP). Which is logical because quotactl() is only supported by the ufs file system. Under 12.0-RELEASE, you get: No such file or directory (ENOENT). As a result, with samba, you can see some error messages claiming "No such file or directory" each time you create or delete a file on a samba share. Created attachment 200570 [details]
C code to test the error returned by quotactl()
Reading the man page of quotactl(), I came to the conclusion that it shouldn't return ENOENT nor ENOTSUP (EOPNOTSUPP) but rather EINVAL error in the case the file system isn't ufs. ENOTSUP seems reserved when the kernel has no support for quota. And kern.features.ufs_quota is enabled both in 11.2 and 12.0 generic kernels. [EINVAL] The cmd argument or the command type is invalid. In Q_GETQUOTASIZE, Q_GETQUOTA, Q_SETQUOTA, and Q_SETUSE, quotas are not currently enabled for this file system. ZFS implements quotactl(2) as of r336017; the man page needs to be updated. Which quota operation is failing in your case? (In reply to Mark Johnston from comment #4) I wrote it in my first post (sorry for the double post by the way): it's Samba which don't handle the error returned by 12.0-RELEASE (ENOENT) and flood tty0 with error messages. There is at least two workarounds to avoid these errors. Details here: https://forums.freebsd.org/threads/samba-quota-errors-logged.68832/ My point is that I don't think it's normal that quotactl() returns ENOENT even if the quota system is now implemented on zfs. I and many people don't use quota. So, in this case, what should be the correct error? Samba handles ENOTSUP and EINVAL errors, not ENOENT. If ENOENT is definitively the suitable error for this case (zfs with no quota enabled), then the Samba code must be patched, for FreeBSD at least. (In reply to Emrion from comment #5) I was wondering specifically which quotactl() operation was returning ENOENT. It's a bit more clear now; we have the following in zfs_getquota(): 146 if (quotaobj == 0 || zfsvfs->z_replay) { 147 error = ENOENT; 148 goto done; 149 } whereas UFS does: 1302 dqvp = ump->um_quotas[type]; 1303 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) { 1304 *dqp = NODQUOT; 1305 UFS_UNLOCK(ump); 1306 return (EINVAL); 1307 } This use of EINVAL isn't documented in the man page. Linux's quotactl() doesn't document it either, but I presume that it always returns EINVAL in this case. (In reply to Mark Johnston from comment #6) Sorry, I didn't understand your question. Now, you've seen the concerned snippet code of Samba or the code I posted here for testing purpose. It's actually Q_GETQUOTA. Thanks for providing the relevant portion of code inside FreeBSD. I found it comes from zfs_vfsops.c. I can just suppose that zfsvfs->z_groupquota_obj and zfsvfs->z_userquota_are null when there is no defined quota for the current user / group. Then, NOENT is returned. Is this a bug? (In reply to Emrion from comment #7) That's a good question. I picked ENOENT because of similarity to other code, but going with UFS compatibility is a stronger argument. The main intended use for zfs quota support was rpc.quotad, and it doesn't care about the errno. Samba logs an error according to if (errno != ENOTSUP && errno != EINVAL) So, to avoid excessive logging, it would be nice to replace the error code with either ENOTSUP or EINVAL. IMHO it makes more sense to use ENOTSUP but this code is not documented in quotactl(2) at all. For UFS compatibility, we should use EINVAL but according to man page, this error code is not applicable. I suggest to use ENOTSUP if there is no zfs quota. (In reply to topical from comment #9) I don't think it makes sense to return EOPNOTSUPP when Q_GETQUOTA fails because no quota is configured. UFS already uses EINVAL in this case, and it is in fact documented (earlier I claimed it wasn't): [EINVAL] The cmd argument or the command type is invalid. In Q_GETQUOTASIZE, Q_GETQUOTA, Q_SETQUOTA, and Q_SETUSE, quotas are not currently enabled for this file system. We should simply modify ZFS to match the documentation. Created attachment 201007 [details]
Return EINVAL if no quotas are present
Obviously I have no objection to this ;).
(In reply to Sean Eric Fagan from comment #11) LGTM, thanks. Just committed as r342928. A commit references this bug: Author: sef Date: Thu Jan 31 22:06:47 UTC 2019 New revision: 343623 URL: https://svnweb.freebsd.org/changeset/base/343623 Log: MFC r342928: Change ZFS quotas to return EINVAL when not present (matches man page). Approved by: mav Reviewed by: markj PR: 234413 Sponsored by: iXsystems Inc Reported by: Emrion <kmachine@free.fr> Changes: _U stable/12/ stable/12/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c A commit references this bug: Author: sef Date: Thu Jan 31 22:08:02 UTC 2019 New revision: 343624 URL: https://svnweb.freebsd.org/changeset/base/343624 Log: MFC r342928: Change ZFS quotas to return EINVAL when not present (matches man page). Approved by: mav Reviewed by: markj PR: 234413 Sponsored by: iXsystems Inc Reported by: Emrion <kmachine@free.fr> Changes: _U stable/11/ stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c Sean, so would it be sufficient to put in tho the client code something like: +#if defined(__FreeBSD__) && ((__FreeBSD_version >= 1102503 && __FreeBSD_version <= 1102506) || (__FreeBSD_version >= 1200072 && __FreeBSD_version <= 1200503) || (__FreeBSD_version >= 1300000 && __FreeBSD_version <= 1300009)) + && errno != ENOENT +#endif If you needed to test for that specific error case, that seems sufficient. But you'd also only want to check for that on a ZFS data set. ^Triage: - Assign to committer that resolved (sef) - Affects some people (Specific FreeBSD version and filesystem ZFS) - Regression between 11.x -> 12.x - Track merges to stable 12/11 |