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