summaryrefslogtreecommitdiff
path: root/common/quota
blob: ff8038251586796268da1722b58086e65d3dacd1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
##/bin/bash
#
# Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
# All Rights Reserved.
#
# 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.
#
# This program is distributed in the hope that it would 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, write the Free Software Foundation,
# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
#
# Functions useful for quota tests
#

#
# checks that the generic quota support in the kernel is enabled
# and that we have valid quota user tools installed.
#
_require_quota()
{
    [ -n $QUOTA_PROG ] || _notrun "Quota user tools not installed"

    case $FSTYP in
    ext2|ext3|ext4|ext4dev|reiserfs)
	if [ ! -d /proc/sys/fs/quota ]; then
	    _notrun "Installed kernel does not support quotas"
	fi
	;;
    gfs2)
	;;
    xfs)
	if [ ! -f /proc/fs/xfs/xqmstat ]; then
	    _notrun "Installed kernel does not support XFS quotas"
        fi
	;;
    *)
	_notrun "disk quotas not supported by this filesystem type: $FSTYP"
	;;
    esac

    # SELinux adds extra xattrs which can mess up our expected output.
    # So, mount with a context, and they won't be created
    # nfs_t is a "liberal" context so we can use it.
    if [ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then
        export SELINUX_MOUNT_OPTIONS="-o context=system_u:object_r:nfs_t:s0"
    fi
}

#
# checks that the XFS quota support in the kernel is enabled
# and that we have valid quota user tools installed.
#
_require_xfs_quota()
{
    src/feature -q $TEST_DEV
    [ $? -ne 0 ] && _notrun "Installed kernel does not support XFS quota"
    [ -n $XFS_QUOTA_PROG ] || _notrun "XFS quota user tools not installed"
}

#
# checks that the XFS project quota support in the kernel is enabled.
#
_require_prjquota()
{
    [ -n "$1" ] && _dev="$1" || _dev="$TEST_DEV"
    src/feature -p $_dev
    [ $? -ne 0 ] && _notrun "Installed kernel does not support project quotas"
}

#
# checks for user nobody in /etc/passwd and /etc/group.
#
_require_nobody()
{
    _cat_passwd | grep -q '^nobody'
    [ $? -ne 0 ] && _notrun "password file does not contain user nobody."

    _cat_group | egrep -q '^no(body|group)'
    [ $? -ne 0 ] && _notrun "group file does not contain nobody/nogroup."
}

# create a file as a specific user (uid)
# takes filename, id, type (u/g/p), blocksize, blockcount
#
_file_as_id()
{
    [ $# != 5 ] && _notrun "broken call to _file_as_id in test $seq"

    parent=`dirname $1`
    if [ $3 = p ]; then
	echo PARENT: xfs_io -r -c "chproj $2" -c "chattr +P" $parent >>$RESULT_DIR/$seq.full
	$XFS_IO_PROG -r -c "chproj $2" -c "chattr +P" $parent >>$RESULT_DIR/$seq.full 2>&1
	magik='$>'	# (irrelevent, above set projid-inherit-on-parent)
    elif [ $3 = u ]; then
	magik='$>'	# perlspeak for effective uid
    elif [ $3 = g ]; then
	magik='$)'	# perlspeak for effective gid
    else
	_notrun "broken type in call to _file_as_id in test $seq"
    fi

    perl <<EOF >>$RESULT_DIR/$seq.full 2>&1
	\$| = 1;
	$magik = $2;
	if ($5 == 0) {
	    print "touch $1";
	    exec "touch $1";
	} else {
	    print "dd if=/dev/zero of=$1 bs=$4 count=$5";
	    exec "dd if=/dev/zero of=$1 bs=$4 count=$5";
	}
EOF
# for debugging the above euid change, try... [need write in cwd]
#	exec "dd if=/dev/zero of=$1 bs=$4 count=$5 >>$RESULT_DIR/$seq.full 2>&1";

    if [ $3 = p ]; then
	echo PARENT: xfs_io -r -c "chproj 0" -c "chattr -P" $parent >>$RESULT_DIR/$seq.full
	$XFS_IO_PROG -r -c "chproj 0" -c "chattr -P" $parent >>$RESULT_DIR/$seq.full 2>&1
    fi
}

_choose_uid()
{
    _cat_passwd | grep '^nobody' | perl -ne '@a = split(/:/); END { printf "id=%d name=%s\n", $a[2],$a[0] }'
}

_choose_gid()
{
    _cat_group | egrep '^no(body|group)' | perl -ne '@a = split(/:/); END { printf "id=%d name=%s\n", $a[2],$a[0] }'
}

_choose_prid()
{
    if [ "X$projid_file" == "X" ]; then
	projid_file=/etc/projid
    fi
    if [ ! -f $projid_file ]; then
	echo 0
	return
    fi
    perl -ne '@a = split(/:/); END { printf "id=%d name=%s\n", $a[1],$a[0] }' \
	$projid_file
}

_qmount()
{
    umount $SCRATCH_DEV >/dev/null 2>&1
    _scratch_mount || _fail "qmount failed"
    chmod ugo+rwx $SCRATCH_MNT
}

_qsetup()
{
    # setup exactly what it is we'll be testing
    enforce=1
    if src/feature -u $SCRATCH_DEV
    then
	type=u ;
	eval `_choose_uid`
	[ ! -f $seq.out ] && ln -s $seq.usrquota $seq.out
    elif src/feature -g $SCRATCH_DEV
    then
	type=g
	eval `_choose_gid`
	[ ! -f $seq.out ] && ln -s $seq.grpquota $seq.out
    elif src/feature -p $SCRATCH_DEV
    then
	type=p
	eval `_choose_prid`
	[ ! -f $seq.out ] && ln -s $seq.prjquota $seq.out
    elif src/feature -U $SCRATCH_DEV
    then
	type=u
	eval `_choose_uid`
	[ ! -f $seq.out ] && ln -s $seq.uqnoenforce $seq.out
	enforce=0
    elif src/feature -G $SCRATCH_DEV
    then
	type=g
	eval `_choose_gid`
	[ ! -f $seq.out ] && ln -s $seq.gqnoenforce $seq.out
	enforce=0
    elif src/feature -P $SCRATCH_DEV
    then
	type=p
	eval `_choose_prid`
	[ ! -f $seq.out ] && ln -s $seq.pqnoenforce $seq.out
	enforce=0
    else
	_notrun "No quota support at mount time"
    fi

    echo "Using output from '" `ls -l $seq.out` "'" >>$RESULT_DIR/$seq.full
    echo "and using type=$type id=$id" >>$RESULT_DIR/$seq.full
}

#
# Ensures only the given quota mount option is used
#
_qmount_option()
{
	# Replace any user defined quota options
	# with the quota option that we want.
	# Simplest to do this rather than delete existing ones first because
	# of the variety of commas and spaces and multiple -o's
	# that we'd have to cater for. Doesn't matter if we have duplicates.
	# Use "QUOTA" string so that we don't have any substring confusion
	# thanks to "quota" which will match with "uquota" and "gquota" etc.
	export MOUNT_OPTIONS=`echo $MOUNT_OPTIONS \
	| sed   -e 's/uquota/QUOTA/g'      \
		-e 's/usrquota/QUOTA/g'    \
		-e 's/gquota/QUOTA/g'      \
		-e 's/grpquota/QUOTA/g'    \
		-e 's/pquota/QUOTA/g'      \
		-e 's/quota/QUOTA/g'       \
		-e 's/uqnoenforce/QUOTA/g' \
		-e 's/gqnoenforce/QUOTA/g' \
		-e 's/pqnoenforce/QUOTA/g' \
		-e 's/qnoenforce/QUOTA/g'  \
		-e "s/QUOTA/$1/g"`

	# Ensure we have the given quota option - duplicates are fine
	export MOUNT_OPTIONS="$MOUNT_OPTIONS -o $1"
	echo "MOUNT_OPTIONS = $MOUNT_OPTIONS" >>$RESULT_DIR/$seq.full
}

_check_quota_usage()
{
	# Sync to get delalloc to disk
	sync

	# kill caches to guarantee removal speculative delalloc
	# XXX: really need an ioctl instead of this big hammer
	echo 3 > /proc/sys/vm/drop_caches

	VFS_QUOTA=0
	case $FSTYP in
	ext2|ext3|ext4|ext4dev|reiserfs)
		VFS_QUOTA=1
		quotaon -f -u -g $SCRATCH_MNT 2>/dev/null
		;;
	*)
		;;
	esac
	repquota -u -n $SCRATCH_MNT  | grep -v "^#0" | _filter_scratch |
		sort >$tmp.user.orig
	repquota -g -n $SCRATCH_MNT  | grep -v "^#0" | _filter_scratch |
		sort >$tmp.group.orig
	if [ $VFS_QUOTA -eq 1 ]; then
		quotacheck -u -g $SCRATCH_MNT 2>/dev/null
	else
		# use XFS method to force quotacheck
		xfs_quota -x -c "off -ug" $SCRATCH_MNT
		_scratch_unmount
		_scratch_mount "-o usrquota,grpquota"
	fi
	repquota -u -n $SCRATCH_MNT  | grep -v "^#0" | _filter_scratch |
		sort >$tmp.user.checked
	repquota -g -n $SCRATCH_MNT  | grep -v "^#0" | _filter_scratch |
		sort >$tmp.group.checked
	if [ $VFS_QUOTA -eq 1 ]; then
		quotaon -u -g $SCRATCH_MNT 2>/dev/null
	fi
	{
		echo "Comparing user usage"
		diff $tmp.user.orig $tmp.user.checked
	} && {
		echo "Comparing group usage"
		diff $tmp.group.orig $tmp.group.checked
	}
}

# make sure this script returns success
/bin/true