File Coverage

linuxapi.c
Criterion Covered Total %
statement 18 113 15.9
branch 11 72 15.2
condition n/a
subroutine n/a
pod n/a
total 29 185 15.6


line stmt bran cond sub pod time code
1             /*
2             ** Linux quotactl wrapper
3             ** Required to support 3 official and intermediate quotactl() versions
4             */
5              
6             #include
7             #include
8             #include
9             #include
10             #include
11             #include
12              
13             #include "myconfig.h"
14              
15             /* API v1 command definitions */
16             #define Q_V1_GETQUOTA 0x0300
17             #define Q_V1_SYNC 0x0600
18             #define Q_V1_SETQLIM 0x0700
19             #define Q_V1_GETSTATS 0x0800
20             /* API v2 command definitions */
21             #define Q_V2_SYNC 0x0600
22             #define Q_V2_SETQLIM 0x0700
23             #define Q_V2_GETQUOTA 0x0D00
24             #define Q_V2_GETSTATS 0x1100
25             /* proc API command definitions */
26             #define Q_V3_SYNC 0x800001
27             #define Q_V3_GETQUOTA 0x800007
28             #define Q_V3_SETQUOTA 0x800008
29              
30             /* Interface versions */
31             #define IFACE_UNSET 0
32             #define IFACE_VFSOLD 1
33             #define IFACE_VFSV0 2
34             #define IFACE_GENERIC 3
35              
36             /* format supported by current kernel */
37             static int kernel_iface = IFACE_UNSET;
38              
39              
40             /*
41             * Quota structure used for communication with userspace via quotactl
42             * Following flags are used to specify which fields are valid
43             */
44             #define QIF_BLIMITS 1
45             #define QIF_SPACE 2
46             #define QIF_ILIMITS 4
47             #define QIF_INODES 8
48             #define QIF_BTIME 16
49             #define QIF_ITIME 32
50             #define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS)
51             #define QIF_USAGE (QIF_SPACE | QIF_INODES)
52             #define QIF_TIMES (QIF_BTIME | QIF_ITIME)
53             #define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES)
54              
55              
56             /*
57             ** Copy of struct declarations in the v2 quota.h header file
58             ** (with structure names changed to avoid conflicts with v2 headers).
59             ** This is required to be able to compile with v1 kernel headers.
60             */
61              
62             /*
63             ** Packed into wrapper for compatibility of 32-bit clients with 64-bit kernels:
64             ** 64-bit compilers add 4 padding bytes at the end of the struct, so a memcpy
65             ** corrupts the 4 bytes following the struct in the 32-bit clients userspace
66             */
67             union dqblk_v3_wrap {
68             struct dqblk_v3 {
69             u_int64_t dqb_bhardlimit;
70             u_int64_t dqb_bsoftlimit;
71             u_int64_t dqb_curspace;
72             u_int64_t dqb_ihardlimit;
73             u_int64_t dqb_isoftlimit;
74             u_int64_t dqb_curinodes;
75             u_int64_t dqb_btime;
76             u_int64_t dqb_itime;
77             u_int32_t dqb_valid;
78             } dqblk;
79             u_int64_t foo[9];
80             };
81              
82              
83             struct dqstats_v2 {
84             u_int32_t lookups;
85             u_int32_t drops;
86             u_int32_t reads;
87             u_int32_t writes;
88             u_int32_t cache_hits;
89             u_int32_t allocated_dquots;
90             u_int32_t free_dquots;
91             u_int32_t syncs;
92             u_int32_t version;
93             };
94              
95              
96             struct dqblk_v2 {
97             unsigned int dqb_ihardlimit;
98             unsigned int dqb_isoftlimit;
99             unsigned int dqb_curinodes;
100             unsigned int dqb_bhardlimit;
101             unsigned int dqb_bsoftlimit;
102             qsize_t dqb_curspace;
103             time_t dqb_btime;
104             time_t dqb_itime;
105             };
106              
107             struct dqblk_v1 {
108             u_int32_t dqb_bhardlimit;
109             u_int32_t dqb_bsoftlimit;
110             u_int32_t dqb_curblocks;
111             u_int32_t dqb_ihardlimit;
112             u_int32_t dqb_isoftlimit;
113             u_int32_t dqb_curinodes;
114             time_t dqb_btime;
115             time_t dqb_itime;
116             };
117              
118              
119              
120             /*
121             ** Check kernel quota version
122             ** Taken from quota-tools 3.08 by Jan Kara
123             */
124 1           static void linuxquota_get_api( void )
125             {
126             #ifndef LINUX_API_VERSION
127             struct stat st;
128              
129 1 50         if (stat("/proc/sys/fs/quota", &st) == 0) {
130 1           kernel_iface = IFACE_GENERIC;
131             }
132             else {
133             struct dqstats_v2 v2_stats;
134             struct sigaction sig;
135             struct sigaction oldsig;
136              
137             /* This signal handling is needed because old kernels send us SIGSEGV as they try to resolve the device */
138 0           sig.sa_handler = SIG_IGN;
139 0           sig.sa_sigaction = NULL;
140 0           sig.sa_flags = 0;
141 0           sigemptyset(&sig.sa_mask);
142 0 0         if (sigaction(SIGSEGV, &sig, &oldsig) < 0) {
143 0           fprintf(stderr, "linuxapi.c warning: cannot set SEGV signal handler: %s\n", strerror(errno));
144 0           goto failure;
145             }
146 0 0         if (quotactl(QCMD(Q_V2_GETSTATS, 0), NULL, 0, (void *)&v2_stats) >= 0) {
147 0           kernel_iface = IFACE_VFSV0;
148             }
149 0 0         else if (errno != ENOSYS && errno != ENOTSUP) {
    0          
150             /* RedHat 7.1 (2.4.2-2) newquota check
151             * Q_V2_GETSTATS in it's old place, Q_GETQUOTA in the new place
152             * (they haven't moved Q_GETSTATS to its new value) */
153 0           int err_stat = 0;
154 0           int err_quota = 0;
155             char tmp[1024]; /* Just temporary buffer */
156              
157 0 0         if (quotactl(QCMD(Q_V1_GETSTATS, 0), NULL, 0, tmp))
158 0           err_stat = errno;
159 0 0         if (quotactl(QCMD(Q_V1_GETQUOTA, 0), "/dev/null", 0, tmp))
160 0           err_quota = errno;
161              
162             /* On a RedHat 2.4.2-2 we expect 0, EINVAL
163             * On a 2.4.x we expect 0, ENOENT
164             * On a 2.4.x-ac we wont get here */
165 0 0         if (err_stat == 0 && err_quota == EINVAL) {
    0          
166 0           kernel_iface = IFACE_VFSV0;
167             }
168             else {
169 0           kernel_iface = IFACE_VFSOLD;
170             }
171             }
172             else {
173             /* This branch is *not* in quota-tools 3.08
174             ** but without it quota version is not correctly
175             ** identified for the original SuSE 8.0 kernel */
176             unsigned int vers_no;
177             FILE * qf;
178              
179 0 0         if ((qf = fopen("/proc/fs/quota", "r"))) {
180 0 0         if (fscanf(qf, "Version %u", &vers_no) == 1) {
181 0 0         if ( (vers_no == (6*10000 + 5*100 + 0)) ||
    0          
182 0           (vers_no == (6*10000 + 5*100 + 1)) ) {
183 0           kernel_iface = IFACE_VFSV0;
184             }
185             }
186 0           fclose(qf);
187             }
188             }
189 0 0         if (sigaction(SIGSEGV, &oldsig, NULL) < 0) {
190 0           fprintf(stderr, "linuxapi.c warning: cannot reset signal handler: %s\n", strerror(errno));
191 0           goto failure;
192             }
193             }
194              
195             failure:
196 1 50         if (kernel_iface == IFACE_UNSET)
197 0           kernel_iface = IFACE_VFSOLD;
198              
199             #else /* defined LINUX_API_VERSION */
200             kernel_iface = LINUX_API_VERSION;
201             #endif
202 1           }
203              
204              
205             /*
206             ** Wrapper for the quotactl(GETQUOTA) call.
207             ** For API v2 the results are copied back into a v1 structure.
208             */
209 62           int linuxquota_query( const char * dev, int uid, int isgrp, struct dqblk * dqb )
210             {
211             int ret;
212              
213 62 50         if (kernel_iface == IFACE_UNSET)
214 0           linuxquota_get_api();
215              
216 62 50         if (kernel_iface == IFACE_GENERIC)
217             {
218             union dqblk_v3_wrap dqb3;
219              
220 62 100         ret = quotactl(QCMD(Q_V3_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)),
221             dev, uid, (caddr_t) &dqb3.dqblk);
222 62 50         if (ret == 0)
223             {
224 0           dqb->dqb_bhardlimit = dqb3.dqblk.dqb_bhardlimit;
225 0           dqb->dqb_bsoftlimit = dqb3.dqblk.dqb_bsoftlimit;
226 0           dqb->dqb_curblocks = dqb3.dqblk.dqb_curspace / DEV_QBSIZE;
227 0           dqb->dqb_ihardlimit = dqb3.dqblk.dqb_ihardlimit;
228 0           dqb->dqb_isoftlimit = dqb3.dqblk.dqb_isoftlimit;
229 0           dqb->dqb_curinodes = dqb3.dqblk.dqb_curinodes;
230 0           dqb->dqb_btime = dqb3.dqblk.dqb_btime;
231 62           dqb->dqb_itime = dqb3.dqblk.dqb_itime;
232             }
233             }
234 0 0         else if (kernel_iface == IFACE_VFSV0)
235             {
236             struct dqblk_v2 dqb2;
237              
238 0 0         ret = quotactl(QCMD(Q_V2_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)),
239             dev, uid, (caddr_t) &dqb2);
240 0 0         if (ret == 0)
241             {
242 0           dqb->dqb_bhardlimit = dqb2.dqb_bhardlimit;
243 0           dqb->dqb_bsoftlimit = dqb2.dqb_bsoftlimit;
244 0           dqb->dqb_curblocks = dqb2.dqb_curspace / DEV_QBSIZE;
245 0           dqb->dqb_ihardlimit = dqb2.dqb_ihardlimit;
246 0           dqb->dqb_isoftlimit = dqb2.dqb_isoftlimit;
247 0           dqb->dqb_curinodes = dqb2.dqb_curinodes;
248 0           dqb->dqb_btime = dqb2.dqb_btime;
249 0           dqb->dqb_itime = dqb2.dqb_itime;
250             }
251             }
252             else /* if (kernel_iface == IFACE_VFSOLD) */
253             {
254             struct dqblk_v1 dqb1;
255              
256 0 0         ret = quotactl(QCMD(Q_V1_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)),
257             dev, uid, (caddr_t) &dqb1);
258 0 0         if (ret == 0)
259             {
260 0           dqb->dqb_bhardlimit = dqb1.dqb_bhardlimit;
261 0           dqb->dqb_bsoftlimit = dqb1.dqb_bsoftlimit;
262 0           dqb->dqb_curblocks = dqb1.dqb_curblocks;
263 0           dqb->dqb_ihardlimit = dqb1.dqb_ihardlimit;
264 0           dqb->dqb_isoftlimit = dqb1.dqb_isoftlimit;
265 0           dqb->dqb_curinodes = dqb1.dqb_curinodes;
266 0           dqb->dqb_btime = dqb1.dqb_btime;
267 0           dqb->dqb_itime = dqb1.dqb_itime;
268             }
269             }
270 62           return ret;
271             }
272              
273             /*
274             ** Wrapper for the quotactl(GETQUOTA) call.
275             ** For API v2 and v3 the parameters are copied into the internal structure.
276             */
277 0           int linuxquota_setqlim( const char * dev, int uid, int isgrp, struct dqblk * dqb )
278             {
279             int ret;
280              
281 0 0         if (kernel_iface == IFACE_UNSET)
282 0           linuxquota_get_api();
283              
284 0 0         if (kernel_iface == IFACE_GENERIC)
285             {
286             union dqblk_v3_wrap dqb3;
287              
288 0           dqb3.dqblk.dqb_bhardlimit = dqb->dqb_bhardlimit;
289 0           dqb3.dqblk.dqb_bsoftlimit = dqb->dqb_bsoftlimit;
290 0           dqb3.dqblk.dqb_curspace = 0;
291 0           dqb3.dqblk.dqb_ihardlimit = dqb->dqb_ihardlimit;
292 0           dqb3.dqblk.dqb_isoftlimit = dqb->dqb_isoftlimit;
293 0           dqb3.dqblk.dqb_curinodes = 0;
294 0           dqb3.dqblk.dqb_btime = dqb->dqb_btime;
295 0           dqb3.dqblk.dqb_itime = dqb->dqb_itime;
296 0           dqb3.dqblk.dqb_valid = (QIF_BLIMITS | QIF_ILIMITS);
297              
298 0 0         ret = quotactl (QCMD(Q_V3_SETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)),
299             dev, uid, (caddr_t) &dqb3.dqblk);
300             }
301 0 0         else if (kernel_iface == IFACE_VFSV0)
302             {
303             struct dqblk_v2 dqb2;
304              
305 0           dqb2.dqb_bhardlimit = dqb->dqb_bhardlimit;
306 0           dqb2.dqb_bsoftlimit = dqb->dqb_bsoftlimit;
307 0           dqb2.dqb_curspace = 0;
308 0           dqb2.dqb_ihardlimit = dqb->dqb_ihardlimit;
309 0           dqb2.dqb_isoftlimit = dqb->dqb_isoftlimit;
310 0           dqb2.dqb_curinodes = 0;
311 0           dqb2.dqb_btime = dqb->dqb_btime;
312 0           dqb2.dqb_itime = dqb->dqb_itime;
313              
314 0 0         ret = quotactl (QCMD(Q_V2_SETQLIM, (isgrp ? GRPQUOTA : USRQUOTA)),
315             dev, uid, (caddr_t) &dqb2);
316             }
317             else /* if (kernel_iface == IFACE_VFSOLD) */
318             {
319             struct dqblk_v1 dqb1;
320              
321 0           dqb1.dqb_bhardlimit = dqb->dqb_bhardlimit;
322 0           dqb1.dqb_bsoftlimit = dqb->dqb_bsoftlimit;
323 0           dqb1.dqb_curblocks = 0;
324 0           dqb1.dqb_ihardlimit = dqb->dqb_ihardlimit;
325 0           dqb1.dqb_isoftlimit = dqb->dqb_isoftlimit;
326 0           dqb1.dqb_curinodes = 0;
327 0           dqb1.dqb_btime = dqb->dqb_btime;
328 0           dqb1.dqb_itime = dqb->dqb_itime;
329              
330 0 0         ret = quotactl (QCMD(Q_V1_SETQLIM, (isgrp ? GRPQUOTA : USRQUOTA)),
331             dev, uid, (caddr_t) &dqb1);
332             }
333              
334 0           return ret;
335             }
336              
337             /*
338             ** Wrapper for the quotactl(SYNC) call.
339             */
340 31           int linuxquota_sync( const char * dev, int isgrp )
341             {
342             int ret;
343              
344 31 100         if (kernel_iface == IFACE_UNSET)
345 1           linuxquota_get_api();
346              
347 31 50         if (kernel_iface == IFACE_GENERIC)
348             {
349 31 50         ret = quotactl (QCMD(Q_V3_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL);
350             }
351 0 0         else if (kernel_iface == IFACE_VFSV0)
352             {
353 0 0         ret = quotactl (QCMD(Q_V2_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL);
354             }
355             else /* if (kernel_iface == IFACE_VFSOLD) */
356             {
357 0 0         ret = quotactl (QCMD(Q_V1_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL);
358             }
359              
360 31           return ret;
361             }
362              
363             #if 0
364             #define DEVICE_PATH "/dev/hda6"
365             main()
366             {
367             struct dqblk dqb;
368              
369             linuxquota_get_api();
370             printf("API=%d\n", kernel_iface);
371              
372             if (linuxquota_sync(DEVICE_PATH, FALSE) != 0)
373             perror("Q_SYNC");
374              
375             if (linuxquota_query(DEVICE_PATH, getuid(), 0, &dqb) == 0)
376             {
377             printf("blocks: usage %d soft %d hard %d expire %s",
378             dqb.dqb_curblocks, dqb.dqb_bhardlimit, dqb.dqb_bsoftlimit,
379             ((dqb.dqb_btime != 0) ? (char*)ctime(&dqb.dqb_btime) : "n/a\n"));
380             printf("inodes: usage %d soft %d hard %d expire %s",
381             dqb.dqb_curinodes, dqb.dqb_ihardlimit, dqb.dqb_isoftlimit,
382             ((dqb.dqb_itime != 0) ? (char*)ctime(&dqb.dqb_itime) : "n/a\n"));
383             }
384             else
385             perror("Q_GETQUOTA");
386             }
387             #endif
388