From owner-freebsd-fs@FreeBSD.ORG Wed May 16 22:17:57 2012 Return-Path: Delivered-To: freebsd-fs@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id BA75E106564A for ; Wed, 16 May 2012 22:17:57 +0000 (UTC) (envelope-from dan@dan.emsphone.com) Received: from email2.allantgroup.com (firebox.emsphone.com [199.67.51.15]) by mx1.freebsd.org (Postfix) with ESMTP id 71AFB8FC14 for ; Wed, 16 May 2012 22:17:57 +0000 (UTC) Received: from dan.emsphone.com (dan.emsphone.com [172.17.17.101]) by email2.allantgroup.com (8.14.4/8.14.4) with ESMTP id q4GMH9VA063749 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 16 May 2012 17:17:10 -0500 (CDT) (envelope-from dan@dan.emsphone.com) Received: from dan.emsphone.com (smmsp@localhost [127.0.0.1]) by dan.emsphone.com (8.14.5/8.14.5) with ESMTP id q4GMH9Qv031465 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 16 May 2012 17:17:09 -0500 (CDT) (envelope-from dan@dan.emsphone.com) Received: (from dan@localhost) by dan.emsphone.com (8.14.5/8.14.5/Submit) id q4GMH9vf031463; Wed, 16 May 2012 17:17:09 -0500 (CDT) (envelope-from dan) Date: Wed, 16 May 2012 17:17:09 -0500 From: Dan Nelson To: Tom Message-ID: <20120516221709.GA1933@dan.emsphone.com> References: <4FB2D099.8040103@bsdunix.ch> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="FCuugMFkClbJLl1L" Content-Disposition: inline In-Reply-To: <4FB2D099.8040103@bsdunix.ch> X-OS: FreeBSD 8.3-PRERELEASE User-Agent: Mutt/1.5.21 (2010-09-15) X-Virus-Scanned: clamav-milter 0.97.2 at email2.allantgroup.com X-Virus-Status: Clean X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.2.6 (email2.allantgroup.com [172.17.19.78]); Wed, 16 May 2012 17:17:10 -0500 (CDT) X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on email2.allantgroup.com X-Scanned-By: MIMEDefang 2.68 on 172.17.19.78 Cc: freebsd-fs@FreeBSD.org Subject: Re: zfs userquota support for rquotad (nfs)? X-BeenThere: freebsd-fs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Filesystems List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 16 May 2012 22:17:57 -0000 --FCuugMFkClbJLl1L Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In the last episode (May 15), Tom said: > Hello > > Has someone tried or is working on an implementation to add ZFS > userquota support to rquotad? > > https://hg.openindiana.org/upstream/illumos/illumos-gate/rev/4f68f041ddcd Here's a simple implementation I coded up a year ago. Instead of trying to link in libzfs on the fly, it simply popens a "zfs get" command to fetch the user's quota info. -- Dan Nelson dnelson@allantgroup.com --FCuugMFkClbJLl1L Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="userquota.diff" Index: rquotad.c =================================================================== --- rquotad.c (revision 234445) +++ rquotad.c (working copy) @@ -36,7 +36,8 @@ void printerr_reply(SVCXPRT *transp); void initfs(void); int getfsquota(long id, char *path, struct dqblk *dqblk); -int hasquota(struct fstab *fs, char **qfnamep); +int hasquota(struct fstab *fs, const char **qfnamep); +static int getzfsquota(uid_t user, char *dataset, struct dqblk *zq); /* * structure containing informations about ufs filesystems @@ -45,6 +46,8 @@ struct fs_stat { struct fs_stat *fs_next; /* next element */ char *fs_file; /* mount point of the filesystem */ + char *fs_spec; /* device name of the filesystem */ + char *fs_type; /* type of the filesystem */ char *qfpathname; /* pathname of the quota file */ dev_t st_dev; /* device of the filesystem */ } fs_stat; @@ -209,17 +212,22 @@ { struct fs_stat *fs_current = NULL; struct fs_stat *fs_next = NULL; - char *qfpathname; + const char *qfpathname; struct fstab *fs; struct stat st; setfsent(); while ((fs = getfsent())) { - if (strcmp(fs->fs_vfstype, "ufs")) + /* Only process UFS or ZFS filesystems */ + if (strcmp(fs->fs_vfstype, "ufs") && strcmp(fs->fs_vfstype, "zfs")) continue; - if (!hasquota(fs, &qfpathname)) + /* Skip UFS filesystems with quotas disabled */ + if ((strcmp(fs->fs_vfstype, "ufs") == 0) && !hasquota(fs, &qfpathname)) continue; + if (strcmp(fs->fs_vfstype, "zfs") == 0) + qfpathname = ""; + fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat)); fs_current->fs_next = fs_next; /* next element */ @@ -227,6 +235,14 @@ malloc(sizeof(char) * (strlen(fs->fs_file) + 1)); strcpy(fs_current->fs_file, fs->fs_file); + fs_current->fs_spec = + malloc(sizeof(char) * (strlen(fs->fs_spec) + 1)); + strcpy(fs_current->fs_spec, fs->fs_spec); + + fs_current->fs_type = + malloc(sizeof(char) * (strlen(fs->fs_vfstype) + 1)); + strcpy(fs_current->fs_type, fs->fs_vfstype); + fs_current->qfpathname = malloc(sizeof(char) * (strlen(qfpathname) + 1)); strcpy(fs_current->qfpathname, qfpathname); @@ -257,10 +273,13 @@ qcmd = QCMD(Q_GETQUOTA, USRQUOTA); for (fs = fs_begin; fs != NULL; fs = fs->fs_next) { - /* where the devise is the same as path */ + /* where the device is the same as path */ if (fs->st_dev != st_path.st_dev) continue; + if (strcmp(fs->fs_type, "zfs") == 0) + return (getzfsquota(id, fs->fs_spec, dqblk)); + /* find the specified filesystem. get and return quota */ if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0) return (1); @@ -300,7 +319,7 @@ * Comes from quota.c, NetBSD 0.9 */ int -hasquota(struct fstab *fs, char **qfnamep) +hasquota(struct fstab *fs, const char **qfnamep) { static char initname, usrname[100]; static char buf[BUFSIZ]; @@ -329,3 +348,43 @@ *qfnamep = buf; return (1); } + +static int +getzfsquota(uid_t user, char *dataset, struct dqblk *zq) +{ + uint64_t userquota, userused; + FILE *zfp; + char buf[64]; + char *cmd; + int failed = 0; + + asprintf(&cmd, "zfs get -Hp -o value userquota@%d,userused@%d %s", user, user, dataset); + if (cmd == NULL) + return (0); + + zfp = popen(cmd, "r"); + free(cmd); + if (zfp == NULL) + return (0); + + if (fgets(buf, sizeof(buf), zfp) == NULL) + failed = 1; + if (!failed && (sscanf(buf, "%lld", &userquota) != 1)) + failed = 1; + if (!failed && (fgets(buf, sizeof(buf), zfp) == NULL)) + failed = 1; + if (!failed && (sscanf(buf, "%lld", &userused) != 1)) + failed = 1; + failed += pclose(zfp); + + if (failed) + return(0); + + bzero(zq, sizeof(struct dqblk)); + + zq->dqb_bhardlimit = userquota / DEV_BSIZE; + zq->dqb_bsoftlimit = userquota / DEV_BSIZE; + zq->dqb_curblocks = userused / DEV_BSIZE; + + return (1); +} --FCuugMFkClbJLl1L--