(root)/
util-linux-2.39/
lib/
caputils.c
       1  /*
       2   * This program is free software; you can redistribute it and/or modify it
       3   * under the terms of the GNU General Public License as published by the
       4   * Free Software Foundation; either version 2, or (at your option) any
       5   * later version.
       6   *
       7   * This program is distributed in the hope that it will be useful, but
       8   * WITHOUT ANY WARRANTY; without even the implied warranty of
       9   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      10   * General Public License for more details.
      11   *
      12   * You should have received a copy of the GNU General Public License along
      13   * with this program; if not, write to the Free Software Foundation, Inc.,
      14   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      15   */
      16  
      17  #include <sys/prctl.h>
      18  #include <stdio.h>
      19  #include <stdlib.h>
      20  #include <limits.h>
      21  #include <errno.h>
      22  
      23  #include "c.h"
      24  #include "caputils.h"
      25  #include "pathnames.h"
      26  #include "procfs.h"
      27  
      28  static int test_cap(unsigned int cap)
      29  {
      30  	/* prctl returns 0 or 1 for valid caps, -1 otherwise */
      31  	return prctl(PR_CAPBSET_READ, cap, 0, 0, 0) >= 0;
      32  }
      33  
      34  static int cap_last_by_bsearch(int *ret)
      35  {
      36  	/* starting with cap=INT_MAX means we always know
      37  	 * that cap1 is invalid after the first iteration */
      38  	int cap = INT_MAX;
      39  	unsigned int cap0 = 0, cap1 = INT_MAX;
      40  
      41  	while ((int)cap0 < cap) {
      42  		if (test_cap(cap))
      43  			cap0 = cap;
      44  		else
      45  			cap1 = cap;
      46  
      47  		cap = (cap0 + cap1) / 2U;
      48  	}
      49  
      50  	*ret = cap;
      51  	return 0;
      52  }
      53  
      54  static int cap_last_by_procfs(int *ret)
      55  {
      56  	FILE *f = fopen(_PATH_PROC_CAPLASTCAP, "r");
      57  	int rc = -EINVAL;
      58  
      59  	*ret = 0;
      60  
      61  	if (f && fd_is_procfs(fileno(f))) {
      62  		int cap;
      63  
      64  		/* we check if the cap after this one really isn't valid */
      65  		if (fscanf(f, "%d", &cap) == 1 &&
      66  		    cap < INT_MAX && !test_cap(cap + 1)) {
      67  
      68  			*ret = cap;
      69  			rc = 0;
      70  		}
      71  	}
      72  
      73  	if (f)
      74  		fclose(f);
      75  	return rc;
      76  }
      77  
      78  int cap_last_cap(void)
      79  {
      80  	static int cap = -1;
      81  
      82  	if (cap != -1)
      83  		return cap;
      84  	if (cap_last_by_procfs(&cap) < 0)
      85  		cap_last_by_bsearch(&cap);
      86  
      87  	return cap;
      88  }
      89  
      90  #ifdef TEST_PROGRAM_CAPUTILS
      91  int main(int argc, char *argv[])
      92  {
      93  	int rc = 0, cap;
      94  
      95  	if (argc < 2) {
      96  		fprintf(stderr, "usage: %1$s --last-by-procfs\n"
      97  				"       %1$s --last-by-bsearch\n"
      98  				"       %1$s --last\n",
      99  				program_invocation_short_name);
     100  		return EXIT_FAILURE;
     101  	}
     102  
     103  	if (strcmp(argv[1], "--last-by-procfs") == 0) {
     104  		rc = cap_last_by_procfs(&cap);
     105  		if (rc == 0)
     106  			printf("last cap: %d\n", cap);
     107  
     108  	} else if (strcmp(argv[1], "--last-by-bsearch") == 0) {
     109  		rc = cap_last_by_bsearch(&cap);
     110  		if (rc == 0)
     111  			printf("last cap: %d\n", cap);
     112  
     113  	} else if (strcmp(argv[1], "--last") == 0)
     114  		printf("last cap: %d\n", cap_last_cap());
     115  
     116  	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
     117  }
     118  #endif