xref: /illumos-gate/usr/src/uts/common/fs/tmpfs/tmp_subr.c (revision 2ca5b6595b95478e6568b0e77c6c83c8a870867a)
1  /*
2   * CDDL HEADER START
3   *
4   * The contents of this file are subject to the terms of the
5   * Common Development and Distribution License, Version 1.0 only
6   * (the "License").  You may not use this file except in compliance
7   * with the License.
8   *
9   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10   * or http://www.opensolaris.org/os/licensing.
11   * See the License for the specific language governing permissions
12   * and limitations under the License.
13   *
14   * When distributing Covered Code, include this CDDL HEADER in each
15   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16   * If applicable, add the following below this CDDL HEADER, with the
17   * fields enclosed by brackets "[]" replaced with your own identifying
18   * information: Portions Copyright [yyyy] [name of copyright owner]
19   *
20   * CDDL HEADER END
21   */
22  /*
23   * Copyright 1989-1999,2001-2003 Sun Microsystems, Inc.  All rights reserved.
24   * Use is subject to license terms.
25   */
26  
27  #pragma ident	"%Z%%M%	%I%	%E% SMI"
28  
29  #include <sys/types.h>
30  #include <sys/errno.h>
31  #include <sys/param.h>
32  #include <sys/t_lock.h>
33  #include <sys/systm.h>
34  #include <sys/sysmacros.h>
35  #include <sys/debug.h>
36  #include <sys/time.h>
37  #include <sys/cmn_err.h>
38  #include <sys/vnode.h>
39  #include <sys/stat.h>
40  #include <sys/vfs.h>
41  #include <sys/cred.h>
42  #include <sys/kmem.h>
43  #include <sys/atomic.h>
44  #include <sys/policy.h>
45  #include <sys/fs/tmp.h>
46  #include <sys/fs/tmpnode.h>
47  
48  #define	MODESHIFT	3
49  
50  int
51  tmp_taccess(void *vtp, int mode, struct cred *cred)
52  {
53  	struct tmpnode *tp = vtp;
54  	int shift = 0;
55  	/*
56  	 * Check access based on owner, group and
57  	 * public permissions in tmpnode.
58  	 */
59  	if (crgetuid(cred) != tp->tn_uid) {
60  		shift += MODESHIFT;
61  		if (groupmember(tp->tn_gid, cred) == 0)
62  			shift += MODESHIFT;
63  	}
64  
65  	/* compute missing mode bits */
66  	mode &= ~(tp->tn_mode << shift);
67  
68  	if (mode == 0)
69  		return (0);
70  
71  	return (secpolicy_vnode_access(cred, TNTOV(tp), tp->tn_uid, mode));
72  }
73  
74  /*
75   * Decide whether it is okay to remove within a sticky directory.
76   * Two conditions need to be met:  write access to the directory
77   * is needed.  In sticky directories, write access is not sufficient;
78   * you can remove entries from a directory only if you own the directory,
79   * if you are privileged, if you own the entry or if they entry is
80   * a plain file and you have write access to that file.
81   * Function returns 0 if remove access is granted.
82   */
83  
84  int
85  tmp_sticky_remove_access(struct tmpnode *dir, struct tmpnode *entry,
86  	struct cred *cr)
87  {
88  	uid_t uid = crgetuid(cr);
89  
90  	if ((dir->tn_mode & S_ISVTX) &&
91  	    uid != dir->tn_uid &&
92  	    uid != entry->tn_uid &&
93  	    (entry->tn_type != VREG ||
94  	    tmp_taccess(entry, VWRITE, cr) != 0))
95  		return (secpolicy_vnode_remove(cr));
96  
97  	return (0);
98  }
99  
100  /*
101   * Allocate zeroed memory if tmpfs_maxkmem has not been exceeded
102   * or the 'musthave' flag is set.  'musthave' allocations should
103   * always be subordinate to normal allocations so that tmpfs_maxkmem
104   * can't be exceeded by more than a few KB.  Example: when creating
105   * a new directory, the tmpnode is a normal allocation; if that
106   * succeeds, the dirents for "." and ".." are 'musthave' allocations.
107   */
108  void *
109  tmp_memalloc(size_t size, int musthave)
110  {
111  	static time_t last_warning;
112  	time_t now;
113  
114  	if (atomic_add_long_nv(&tmp_kmemspace, size) < tmpfs_maxkmem ||
115  	    musthave)
116  		return (kmem_zalloc(size, KM_SLEEP));
117  
118  	atomic_add_long(&tmp_kmemspace, -size);
119  	now = gethrestime_sec();
120  	if (last_warning != now) {
121  		last_warning = now;
122  		cmn_err(CE_WARN, "tmp_memalloc: tmpfs over memory limit");
123  	}
124  	return (NULL);
125  }
126  
127  void
128  tmp_memfree(void *cp, size_t size)
129  {
130  	kmem_free(cp, size);
131  	atomic_add_long(&tmp_kmemspace, -size);
132  }
133  
134  /*
135   * Convert a string containing a number (number of bytes) to a pgcnt_t,
136   * containing the corresponding number of pages. On 32-bit kernels, the
137   * maximum value encoded in 'str' is PAGESIZE * ULONG_MAX, while the value
138   * returned in 'maxpg' is at most ULONG_MAX.
139   *
140   * If the number is followed by a "k" or "K", the value is converted from
141   * kilobytes to bytes.  If it is followed by an "m" or "M" it is converted
142   * from megabytes to bytes.  If it is not followed by a character it is
143   * assumed to be in bytes. Multiple letter options are allowed, so for instance
144   * '2mk' is interpreted as 2gb.
145   *
146   * Parse and overflow errors are detected and a non-zero number returned on
147   * error.
148   */
149  
150  int
151  tmp_convnum(char *str, pgcnt_t *maxpg)
152  {
153  	uint64_t num = 0, oldnum;
154  #ifdef _LP64
155  	uint64_t max_bytes = ULONG_MAX;
156  #else
157  	uint64_t max_bytes = PAGESIZE * (uint64_t)ULONG_MAX;
158  #endif
159  	char *c;
160  
161  	if (str == NULL)
162  		return (EINVAL);
163  	c = str;
164  
165  	/*
166  	 * Convert str to number
167  	 */
168  	while ((*c >= '0') && (*c <= '9')) {
169  		oldnum = num;
170  		num = num * 10 + (*c++ - '0');
171  		if (oldnum > num) /* overflow */
172  			return (EINVAL);
173  	}
174  
175  	/*
176  	 * Terminate on null
177  	 */
178  	while (*c != '\0') {
179  		switch (*c++) {
180  
181  		/*
182  		 * convert from kilobytes
183  		 */
184  		case 'k':
185  		case 'K':
186  			if (num > max_bytes / 1024) /* will overflow */
187  				return (EINVAL);
188  			num *= 1024;
189  			break;
190  
191  		/*
192  		 * convert from megabytes
193  		 */
194  		case 'm':
195  		case 'M':
196  			if (num > max_bytes / (1024 * 1024)) /* will overflow */
197  				return (EINVAL);
198  			num *= 1024 * 1024;
199  			break;
200  
201  		default:
202  			return (EINVAL);
203  		}
204  	}
205  
206  	/*
207  	 * Since btopr() rounds up to page granularity, this round-up can
208  	 * cause an overflow only if 'num' is between (max_bytes - PAGESIZE)
209  	 * and (max_bytes). In this case the resulting number is zero, which
210  	 * is what we check for below.
211  	 */
212  	if ((*maxpg = (pgcnt_t)btopr(num)) == 0 && num != 0)
213  		return (EINVAL);
214  	return (0);
215  }
216