/* * ZFS dfree Quotas VFS module for Samba 3.6 * * Copyright (C) Björn Baumbach , 2013 * * Based on the work of Mike Moya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "includes.h" #include "smbd/smbd.h" #include /* Get ZFS quota for user */ static bool zfs_quotas(const char *zfspath, uid_t euser_id, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize) { libzfs_handle_t *zfs; zfs_handle_t *zhp; uint64_t userquota; uint64_t userused; char property[24]; char cwdpath[PATH_MAX+1]; struct mnttab mnt; SMB_STRUCT_STAT sbuf; FILE *fp_mnttab; SMB_DEV_T devno; getcwd(cwdpath, PATH_MAX+1); if (sys_stat(zfspath, &sbuf, false) == -1) { return false; } devno = sbuf.st_ex_dev; DEBUG(5, ("disk_quotas: looking for path \"%s\" in \"%s\" devno=%x\n", zfspath, cwdpath, (unsigned int)devno)); /* Get zfs device from MNTTAB */ fp_mnttab = sys_fopen(MNTTAB, "r"); if (fp_mnttab == NULL) { return false; } while (getmntent(fp_mnttab, &mnt) == 0) { if (sys_stat(mnt.mnt_mountp, &sbuf, false) == -1) { continue; } if((sbuf.st_ex_dev == devno) && strcmp(mnt.mnt_fstype, "zfs") == 0) { DEBUG(10, ("zfs_quotas: Using mountp=%s device=%s\n", mnt.mnt_mountp, mnt.mnt_special)); break; } } fclose(fp_mnttab); zfs = libzfs_init(); if (zfs == NULL) { DEBUG(1, ("zfs_quotas: Cannot initialize libzfs\n")); return false; } errno = 0; zhp = zfs_open(zfs, mnt.mnt_special, ZFS_TYPE_DATASET); if (zhp == NULL) { DEBUG(1, ("zfs_quotas: Cannot open zfs handle \"%s\": %s\n", mnt.mnt_special, strerror(errno))); libzfs_fini(zfs); return false; } /* Get quota */ snprintf(property, sizeof(property), "userquota@%u", euser_id); if (zfs_prop_get_userquota_int(zhp, property, &userquota)) { DEBUG(10, ("zfs_quotas: Cannot open zfs property \"%s\"\n", property)); zfs_close(zhp); libzfs_fini(zfs); return false; } /* Get used */ snprintf(property, sizeof(property), "userused@%u", euser_id); if (zfs_prop_get_userquota_int(zhp, property, &userused)) { DEBUG(10, ("zfs_quotas: Cannot open zfs property \"%s\"\n", property)); zfs_close(zhp); libzfs_fini(zfs); return false; } /* Close handles */ zfs_close(zhp); libzfs_fini(zfs); /* No quota? */ if (userquota == 0) { return false; } /* Update results */ *bsize = DEV_BSIZE; *dsize = userquota / DEV_BSIZE; if (userused > userquota) { *dfree = 0; *dsize = userused / DEV_BSIZE; } else { *dfree = (userquota - userused) / DEV_BSIZE; } DEBUG(10, ("zfs_quotas: For \"%s\" returning bsize %u, dfree %u, dsize %u\n", mnt.mnt_mountp, *bsize, *dfree, *dsize)); DEBUG(10, ("zfs_quotas: End of zfs_quotas\n")); return true; } static uint64_t zfs_dfree_disk_free(vfs_handle_struct *handle, const char *path, bool small_query, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize) { if (zfs_quotas(path, geteuid(), bsize, dfree, dsize)) { return *dfree; } return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query, bsize, dfree, dsize); } static struct vfs_fn_pointers vfs_zfs_dfree_fns = { .disk_free = zfs_dfree_disk_free, }; NTSTATUS vfs_zfs_dfree_init(void); NTSTATUS vfs_zfs_dfree_init(void) { return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "zfs_dfree", &vfs_zfs_dfree_fns); }