Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | * | |
9 | * Portions Copyright (c) 2003 Apple Computer, Inc. | |
10 | */ | |
11 | ||
12 | /* | |
13 | * afs_vnop_attrs.c - setattr and getattr vnodeops | |
14 | * | |
15 | * Implements: | |
16 | * afs_CopyOutAttrs | |
17 | * afs_getattr | |
18 | * afs_VAttrToAS | |
19 | * afs_setattr | |
20 | * afs_CreateAttr | |
21 | * afs_DestroyAttr | |
22 | * | |
23 | */ | |
24 | ||
25 | #include <afsconfig.h> | |
26 | #include "afs/param.h" | |
27 | ||
28 | ||
29 | #include "afs/sysincludes.h" /* Standard vendor system headers */ | |
30 | #include "afsincludes.h" /* Afs-based standard headers */ | |
31 | #include "afs/afs_stats.h" /* statistics */ | |
32 | #include "afs/afs_cbqueue.h" | |
33 | #include "afs/nfsclient.h" | |
34 | #include "afs/afs_osidnlc.h" | |
35 | ||
36 | extern afs_rwlock_t afs_xcbhash; | |
37 | struct afs_exporter *afs_nfsexporter; | |
38 | extern struct vcache *afs_globalVp; | |
39 | #if defined(AFS_HPUX110_ENV) | |
40 | extern struct vfs *afs_globalVFS; | |
41 | #endif | |
42 | ||
43 | ||
44 | /* copy out attributes from cache entry */ | |
45 | int | |
46 | afs_CopyOutAttrs(struct vcache *avc, struct vattr *attrs) | |
47 | { | |
48 | struct volume *tvp; | |
49 | struct cell *tcell; | |
50 | #if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV) | |
51 | struct vnode *vp = AFSTOV(avc); | |
52 | #endif | |
53 | int fakedir = 0; | |
54 | ||
55 | AFS_STATCNT(afs_CopyOutAttrs); | |
56 | if (afs_fakestat_enable && avc->mvstat == AFS_MVSTAT_MTPT) | |
57 | fakedir = 1; | |
58 | attrs->va_type = fakedir ? VDIR : vType(avc); | |
59 | #if defined(AFS_SGI_ENV) || defined(AFS_AIX32_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DARWIN_ENV) | |
60 | attrs->va_mode = fakedir ? S_IFDIR | 0755 : (mode_t) (avc->f.m.Mode & 0xffff); | |
61 | #else | |
62 | attrs->va_mode = fakedir ? VDIR | 0755 : avc->f.m.Mode; | |
63 | #endif | |
64 | ||
65 | if (avc->f.m.Mode & (VSUID | VSGID)) { | |
66 | /* setuid or setgid, make sure we're allowed to run them from this cell */ | |
67 | tcell = afs_GetCell(avc->f.fid.Cell, 0); | |
68 | if (tcell && (tcell->states & CNoSUID)) | |
69 | attrs->va_mode &= ~(VSUID | VSGID); | |
70 | } | |
71 | #if defined(AFS_DARWIN_ENV) | |
72 | { | |
73 | if (!afs_darwin_realmodes) { | |
74 | /* Mac OS X uses the mode bits to determine whether a file or | |
75 | * directory is accessible, and believes them, even though under | |
76 | * AFS they're almost assuredly wrong, especially if the local uid | |
77 | * does not match the AFS ID. So we set the mode bits | |
78 | * conservatively. | |
79 | */ | |
80 | if (S_ISDIR(attrs->va_mode)) { | |
81 | /* all access bits need to be set for directories, since even | |
82 | * a mode 0 directory can still be used normally. | |
83 | */ | |
84 | attrs->va_mode |= ACCESSPERMS; | |
85 | } else { | |
86 | /* for other files, replicate the user bits to group and other */ | |
87 | mode_t ubits = (attrs->va_mode & S_IRWXU) >> 6; | |
88 | attrs->va_mode |= ubits | (ubits << 3); | |
89 | } | |
90 | } | |
91 | } | |
92 | #endif /* AFS_DARWIN_ENV */ | |
93 | attrs->va_uid = fakedir ? 0 : avc->f.m.Owner; | |
94 | attrs->va_gid = fakedir ? 0 : avc->f.m.Group; /* yeah! */ | |
95 | #if defined(AFS_SUN5_ENV) | |
96 | attrs->va_fsid = AFSTOV(avc)->v_vfsp->vfs_fsid.val[0]; | |
97 | #elif defined(AFS_DARWIN80_ENV) | |
98 | VATTR_RETURN(attrs, va_fsid, vfs_statfs(vnode_mount(AFSTOV(avc)))->f_fsid.val[0]); | |
99 | #elif defined(AFS_DARWIN_ENV) | |
100 | attrs->va_fsid = avc->v->v_mount->mnt_stat.f_fsid.val[0]; | |
101 | #else /* ! AFS_DARWIN_ENV */ | |
102 | attrs->va_fsid = 1; | |
103 | #endif | |
104 | if (avc->mvstat == AFS_MVSTAT_ROOT) { | |
105 | tvp = afs_GetVolume(&avc->f.fid, 0, READ_LOCK); | |
106 | /* The mount point's vnode. */ | |
107 | if (tvp) { | |
108 | attrs->va_nodeid = | |
109 | afs_calc_inum(tvp->mtpoint.Cell, | |
110 | tvp->mtpoint.Fid.Volume, | |
111 | tvp->mtpoint.Fid.Vnode); | |
112 | if (FidCmp(&afs_rootFid, &avc->f.fid) && !attrs->va_nodeid) | |
113 | attrs->va_nodeid = 2; | |
114 | afs_PutVolume(tvp, READ_LOCK); | |
115 | } else | |
116 | attrs->va_nodeid = 2; | |
117 | } else | |
118 | attrs->va_nodeid = | |
119 | afs_calc_inum(avc->f.fid.Cell, | |
120 | avc->f.fid.Fid.Volume, | |
121 | avc->f.fid.Fid.Vnode); | |
122 | attrs->va_nodeid &= 0x7fffffff; /* Saber C hates negative inode #s! */ | |
123 | attrs->va_nlink = fakedir ? 100 : avc->f.m.LinkCount; | |
124 | attrs->va_size = fakedir ? 4096 : avc->f.m.Length; | |
125 | #if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV) | |
126 | vnode_pager_setsize(vp, (u_long) attrs->va_size); | |
127 | #endif | |
128 | attrs->va_atime.tv_sec = attrs->va_mtime.tv_sec = attrs->va_ctime.tv_sec = | |
129 | fakedir ? 0 : (int)avc->f.m.Date; | |
130 | /* set microseconds to be dataversion # so that we approximate NFS-style | |
131 | * use of mtime as a dataversion #. We take it mod 512K because | |
132 | * microseconds *must* be less than a million, and 512K is the biggest | |
133 | * power of 2 less than such. DataVersions are typically pretty small | |
134 | * anyway, so the difference between 512K and 1000000 shouldn't matter | |
135 | * much, and "&" is a lot faster than "%". | |
136 | */ | |
137 | #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV) | |
138 | /* nfs on these systems puts an 0 in nsec and stores the nfs usec (aka | |
139 | * dataversion) in va_gen */ | |
140 | ||
141 | attrs->va_atime.tv_nsec = attrs->va_mtime.tv_nsec = | |
142 | attrs->va_ctime.tv_nsec = 0; | |
143 | attrs->va_gen = hgetlo(avc->f.m.DataVersion); | |
144 | #elif defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV) || defined(AFS_OBSD_ENV) || defined(AFS_NBSD_ENV) | |
145 | attrs->va_atime.tv_nsec = attrs->va_mtime.tv_nsec = | |
146 | attrs->va_ctime.tv_nsec = | |
147 | (hgetlo(avc->f.m.DataVersion) & 0x7ffff) * 1000; | |
148 | #else | |
149 | attrs->va_atime.tv_usec = attrs->va_mtime.tv_usec = | |
150 | attrs->va_ctime.tv_usec = (hgetlo(avc->f.m.DataVersion) & 0x7ffff); | |
151 | #endif | |
152 | #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
153 | attrs->va_flags = 0; | |
154 | #endif | |
155 | #if defined(AFS_SGI_ENV) || defined(AFS_SUN5_ENV) | |
156 | attrs->va_blksize = AFS_BLKSIZE; /* XXX Was 8192 XXX */ | |
157 | #else | |
158 | attrs->va_blocksize = AFS_BLKSIZE; /* XXX Was 8192 XXX */ | |
159 | #endif | |
160 | attrs->va_rdev = 1; | |
161 | #if defined(AFS_HPUX110_ENV) | |
162 | if (afs_globalVFS) | |
163 | attrs->va_fstype = afs_globalVFS->vfs_mtype; | |
164 | #endif | |
165 | ||
166 | /* | |
167 | * Below return 0 (and not 1) blocks if the file is zero length. This conforms | |
168 | * better with the other filesystems that do return 0. | |
169 | */ | |
170 | #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
171 | attrs->va_bytes = (attrs->va_size ? (attrs->va_size + 1023) : 1024); | |
172 | #ifdef va_bytes_rsv | |
173 | attrs->va_bytes_rsv = -1; | |
174 | #endif | |
175 | #elif defined(AFS_HPUX_ENV) | |
176 | attrs->va_blocks = (attrs->va_size ? ((attrs->va_size + 1023)>>10) : 0); | |
177 | #elif defined(AFS_SGI_ENV) | |
178 | attrs->va_blocks = BTOBB(attrs->va_size); | |
179 | #elif defined(AFS_SUN5_ENV) | |
180 | attrs->va_nblocks = (attrs->va_size ? ((attrs->va_size + 1023)>>10)<<1:0); | |
181 | #else /* everything else */ | |
182 | attrs->va_blocks = (attrs->va_size ? ((attrs->va_size + 1023)>>10)<<1:0); | |
183 | #endif | |
184 | return 0; | |
185 | } | |
186 | ||
187 | ||
188 | ||
189 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
190 | int | |
191 | afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, int flags, | |
192 | afs_ucred_t *acred) | |
193 | #else | |
194 | int | |
195 | afs_getattr(OSI_VC_DECL(avc), struct vattr *attrs, afs_ucred_t *acred) | |
196 | #endif | |
197 | { | |
198 | afs_int32 code; | |
199 | struct vrequest *treq = NULL; | |
200 | struct unixuser *au; | |
201 | int inited = 0; | |
202 | OSI_VC_CONVERT(avc); | |
203 | ||
204 | AFS_STATCNT(afs_getattr); | |
205 | afs_Trace2(afs_iclSetp, CM_TRACE_GETATTR, ICL_TYPE_POINTER, avc, | |
206 | ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length)); | |
207 | ||
208 | if (afs_fakestat_enable && avc->mvstat == AFS_MVSTAT_MTPT) { | |
209 | struct afs_fakestat_state fakestat; | |
210 | struct vrequest *ureq = NULL; | |
211 | ||
212 | code = afs_CreateReq(&ureq, acred); | |
213 | if (code) { | |
214 | return code; | |
215 | } | |
216 | afs_InitFakeStat(&fakestat); | |
217 | code = afs_TryEvalFakeStat(&avc, &fakestat, ureq); | |
218 | if (code) { | |
219 | afs_PutFakeStat(&fakestat); | |
220 | afs_DestroyReq(ureq); | |
221 | return code; | |
222 | } | |
223 | ||
224 | code = afs_CopyOutAttrs(avc, attrs); | |
225 | afs_PutFakeStat(&fakestat); | |
226 | afs_DestroyReq(ureq); | |
227 | return code; | |
228 | } | |
229 | #if defined(AFS_SUN5_ENV) | |
230 | if (flags & ATTR_HINT) { | |
231 | code = afs_CopyOutAttrs(avc, attrs); | |
232 | return code; | |
233 | } | |
234 | #endif | |
235 | #if defined(AFS_DARWIN_ENV) && !defined(AFS_DARWIN80_ENV) | |
236 | if (avc->f.states & CUBCinit) { | |
237 | code = afs_CopyOutAttrs(avc, attrs); | |
238 | return code; | |
239 | } | |
240 | #endif | |
241 | ||
242 | AFS_DISCON_LOCK(); | |
243 | ||
244 | if (afs_shuttingdown != AFS_RUNNING) { | |
245 | AFS_DISCON_UNLOCK(); | |
246 | return EIO; | |
247 | } | |
248 | ||
249 | if (!(avc->f.states & CStatd)) { | |
250 | if (!(code = afs_CreateReq(&treq, acred))) { | |
251 | code = afs_VerifyVCache2(avc, treq); | |
252 | inited = 1; | |
253 | } | |
254 | } else | |
255 | code = 0; | |
256 | ||
257 | #if defined(AFS_SUN5_ENV) | |
258 | if (code == 0) | |
259 | osi_FlushPages(avc, acred); | |
260 | #endif | |
261 | ||
262 | if (code == 0) { | |
263 | osi_FlushText(avc); /* only needed to flush text if text locked last time */ | |
264 | code = afs_CopyOutAttrs(avc, attrs); | |
265 | ||
266 | if (afs_nfsexporter) { | |
267 | if (!inited) { | |
268 | if ((code = afs_CreateReq(&treq, acred))) { | |
269 | return code; | |
270 | } | |
271 | inited = 1; | |
272 | } | |
273 | if (AFS_NFSXLATORREQ(acred)) { | |
274 | if ((vType(avc) != VDIR) | |
275 | && !afs_AccessOK(avc, PRSFS_READ, treq, | |
276 | CHECK_MODE_BITS | | |
277 | CMB_ALLOW_EXEC_AS_READ)) { | |
278 | afs_DestroyReq(treq); | |
279 | return EACCES; | |
280 | } | |
281 | } | |
282 | if ((au = afs_FindUser(treq->uid, -1, READ_LOCK))) { | |
283 | struct afs_exporter *exporter = au->exporter; | |
284 | ||
285 | if (exporter && !(afs_nfsexporter->exp_states & EXP_UNIXMODE)) { | |
286 | unsigned int ubits; | |
287 | /* | |
288 | * If the remote user wishes to enforce default Unix mode semantics, | |
289 | * like in the nfs exporter case, we OR in the user bits | |
290 | * into the group and other bits. We need to do this | |
291 | * because there is no RFS_ACCESS call and thus nfs | |
292 | * clients implement nfs_access by interpreting the | |
293 | * mode bits in the traditional way, which of course | |
294 | * loses with afs. | |
295 | */ | |
296 | ubits = (attrs->va_mode & 0700) >> 6; | |
297 | attrs->va_mode = attrs->va_mode | ubits | (ubits << 3); | |
298 | /* If it's the root of AFS, replace the inode number with the | |
299 | * inode number of the mounted on directory; otherwise this | |
300 | * confuses getwd()... */ | |
301 | #ifdef AFS_LINUX22_ENV | |
302 | if (avc == afs_globalVp) { | |
303 | struct inode *ip = AFSTOV(avc)->i_sb->s_root->d_inode; | |
304 | attrs->va_nodeid = ip->i_ino; /* VTOI()? */ | |
305 | } | |
306 | #else | |
307 | if ( | |
308 | #if defined(AFS_DARWIN_ENV) | |
309 | vnode_isvroot(AFSTOV(avc)) | |
310 | #elif defined(AFS_NBSD50_ENV) | |
311 | AFSTOV(avc)->v_vflag & VV_ROOT | |
312 | #else | |
313 | AFSTOV(avc)->v_flag & VROOT | |
314 | #endif | |
315 | ) { | |
316 | struct vnode *vp = AFSTOV(avc); | |
317 | ||
318 | #ifdef AFS_DARWIN80_ENV | |
319 | /* XXX vp = vnode_mount(vp)->mnt_vnodecovered; */ | |
320 | vp = 0; | |
321 | #else | |
322 | vp = vp->v_vfsp->vfs_vnodecovered; | |
323 | if (vp) { /* Ignore weird failures */ | |
324 | #ifdef AFS_SGI62_ENV | |
325 | attrs->va_nodeid = VnodeToIno(vp); | |
326 | #else | |
327 | struct inode *ip; | |
328 | ||
329 | ip = (struct inode *)VTOI(vp); | |
330 | if (ip) /* Ignore weird failures */ | |
331 | attrs->va_nodeid = ip->i_number; | |
332 | #endif | |
333 | } | |
334 | #endif | |
335 | } | |
336 | #endif /* AFS_LINUX22_ENV */ | |
337 | } | |
338 | afs_PutUser(au, READ_LOCK); | |
339 | } | |
340 | } | |
341 | } | |
342 | ||
343 | AFS_DISCON_UNLOCK(); | |
344 | ||
345 | if (!code) { | |
346 | afs_DestroyReq(treq); | |
347 | return 0; | |
348 | } | |
349 | code = afs_CheckCode(code, treq, 14); | |
350 | afs_DestroyReq(treq); | |
351 | return code; | |
352 | } | |
353 | ||
354 | /* convert a Unix request into a status store request */ | |
355 | int | |
356 | afs_VAttrToAS(struct vcache *avc, struct vattr *av, | |
357 | struct AFSStoreStatus *as) | |
358 | { | |
359 | int mask; | |
360 | mask = 0; | |
361 | ||
362 | AFS_STATCNT(afs_VAttrToAS); | |
363 | #if defined(AFS_DARWIN80_ENV) | |
364 | if (VATTR_IS_ACTIVE(av, va_mode)) { | |
365 | #elif defined(AFS_AIX_ENV) | |
366 | /* Boy, was this machine dependent bogosity hard to swallow????.... */ | |
367 | if (av->va_mode != -1) { | |
368 | #elif defined(AFS_LINUX22_ENV) || defined(UKERNEL) | |
369 | if (av->va_mask & ATTR_MODE) { | |
370 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
371 | if (av->va_mask & AT_MODE) { | |
372 | #elif defined(AFS_XBSD_ENV) | |
373 | if (av->va_mode != (mode_t)VNOVAL) { | |
374 | #else | |
375 | if (av->va_mode != ((unsigned short)-1)) { | |
376 | #endif | |
377 | mask |= AFS_SETMODE; | |
378 | as->UnixModeBits = av->va_mode & 0xffff; | |
379 | if (avc->f.states & CForeign) { | |
380 | ObtainWriteLock(&avc->lock, 127); | |
381 | afs_FreeAllAxs(&(avc->Access)); | |
382 | ReleaseWriteLock(&avc->lock); | |
383 | } | |
384 | } | |
385 | #if defined(AFS_DARWIN80_ENV) | |
386 | if (VATTR_IS_ACTIVE(av, va_gid)) { | |
387 | #elif defined(AFS_LINUX22_ENV) || defined(UKERNEL) | |
388 | if (av->va_mask & ATTR_GID) { | |
389 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
390 | if (av->va_mask & AT_GID) { | |
391 | #elif defined(AFS_HPUX_ENV) | |
392 | #if defined(AFS_HPUX102_ENV) | |
393 | if (av->va_gid != GID_NO_CHANGE) { | |
394 | #else | |
395 | if (av->va_gid != ((unsigned short)-1)) { | |
396 | #endif | |
397 | #elif defined(AFS_XBSD_ENV) | |
398 | if (av->va_gid != (gid_t)VNOVAL) { | |
399 | #else | |
400 | if (av->va_gid != -1) { | |
401 | #endif /* AFS_LINUX22_ENV */ | |
402 | mask |= AFS_SETGROUP; | |
403 | as->Group = av->va_gid; | |
404 | } | |
405 | #if defined(AFS_DARWIN80_ENV) | |
406 | if (VATTR_IS_ACTIVE(av, va_uid)) { | |
407 | #elif defined(AFS_LINUX22_ENV) || defined(UKERNEL) | |
408 | if (av->va_mask & ATTR_UID) { | |
409 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
410 | if (av->va_mask & AT_UID) { | |
411 | #elif defined(AFS_HPUX_ENV) | |
412 | #if defined(AFS_HPUX102_ENV) | |
413 | if (av->va_uid != UID_NO_CHANGE) { | |
414 | #elif defined(AFS_XBSD_ENV) | |
415 | if (av->va_uid != (uid_t)VNOVAL) { | |
416 | #else | |
417 | if (av->va_uid != ((unsigned short)-1)) { | |
418 | #endif | |
419 | #else | |
420 | if (av->va_uid != -1) { | |
421 | #endif /* AFS_LINUX22_ENV */ | |
422 | mask |= AFS_SETOWNER; | |
423 | as->Owner = av->va_uid; | |
424 | } | |
425 | #if defined(AFS_DARWIN80_ENV) | |
426 | if (VATTR_IS_ACTIVE(av, va_modify_time)) { | |
427 | #elif defined(AFS_LINUX22_ENV) || defined(UKERNEL) | |
428 | if (av->va_mask & ATTR_MTIME) { | |
429 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
430 | if (av->va_mask & AT_MTIME) { | |
431 | #else | |
432 | if (av->va_mtime.tv_sec != -1) { | |
433 | #endif | |
434 | mask |= AFS_SETMODTIME; | |
435 | #ifndef AFS_SGI_ENV | |
436 | #if defined(AFS_SUN5_ENV) || defined(AFS_AIX41_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV) | |
437 | if (av->va_mtime.tv_nsec == -1) | |
438 | #else | |
439 | if (av->va_mtime.tv_usec == -1) | |
440 | #endif | |
441 | as->ClientModTime = osi_Time(); /* special Sys V compat hack for Suns */ | |
442 | else | |
443 | #endif | |
444 | as->ClientModTime = av->va_mtime.tv_sec; | |
445 | } | |
446 | as->Mask = mask; | |
447 | return 0; | |
448 | } | |
449 | ||
450 | /* We don't set CDirty bit in avc->f.states because setattr calls WriteVCache | |
451 | * synchronously, therefore, it's not needed. | |
452 | */ | |
453 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
454 | int | |
455 | afs_setattr(OSI_VC_DECL(avc), struct vattr *attrs, int flags, | |
456 | afs_ucred_t *acred) | |
457 | #else | |
458 | int | |
459 | afs_setattr(OSI_VC_DECL(avc), struct vattr *attrs, | |
460 | afs_ucred_t *acred) | |
461 | #endif | |
462 | { | |
463 | struct vrequest *treq = NULL; | |
464 | struct AFSStoreStatus astat; | |
465 | afs_int32 code; | |
466 | #if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV) | |
467 | struct vnode *vp = AFSTOV(avc); | |
468 | #endif | |
469 | struct afs_fakestat_state fakestate; | |
470 | OSI_VC_CONVERT(avc); | |
471 | ||
472 | AFS_STATCNT(afs_setattr); | |
473 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) || defined(AFS_LINUX22_ENV) | |
474 | afs_Trace4(afs_iclSetp, CM_TRACE_SETATTR, ICL_TYPE_POINTER, avc, | |
475 | ICL_TYPE_INT32, attrs->va_mask, ICL_TYPE_OFFSET, | |
476 | ICL_HANDLE_OFFSET(attrs->va_size), ICL_TYPE_OFFSET, | |
477 | ICL_HANDLE_OFFSET(avc->f.m.Length)); | |
478 | #else | |
479 | afs_Trace4(afs_iclSetp, CM_TRACE_SETATTR, ICL_TYPE_POINTER, avc, | |
480 | ICL_TYPE_INT32, attrs->va_mode, ICL_TYPE_OFFSET, | |
481 | ICL_HANDLE_OFFSET(attrs->va_size), ICL_TYPE_OFFSET, | |
482 | ICL_HANDLE_OFFSET(avc->f.m.Length)); | |
483 | #endif | |
484 | if ((code = afs_CreateReq(&treq, acred))) | |
485 | return code; | |
486 | ||
487 | memset(&astat, 0, sizeof(astat)); | |
488 | ||
489 | AFS_DISCON_LOCK(); | |
490 | ||
491 | afs_InitFakeStat(&fakestate); | |
492 | code = afs_EvalFakeStat(&avc, &fakestate, treq); | |
493 | if (code) | |
494 | goto done; | |
495 | ||
496 | if (avc->f.states & CRO) { | |
497 | code = EROFS; | |
498 | goto done; | |
499 | } | |
500 | #if defined(AFS_SGI_ENV) | |
501 | /* ignore ATTR_LAZY calls - they are really only for keeping | |
502 | * the access/mtime of mmaped files up to date | |
503 | */ | |
504 | if (flags & ATTR_LAZY) | |
505 | goto done; | |
506 | #endif | |
507 | /* if file size has changed, we need write access, otherwise (e.g. | |
508 | * chmod) give it a shot; if it fails, we'll discard the status | |
509 | * info. | |
510 | */ | |
511 | #if defined(AFS_DARWIN80_ENV) | |
512 | if (VATTR_IS_ACTIVE(attrs, va_data_size)) { | |
513 | #elif defined(AFS_LINUX22_ENV) || defined(UKERNEL) | |
514 | if (attrs->va_mask & ATTR_SIZE) { | |
515 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
516 | if (attrs->va_mask & AT_SIZE) { | |
517 | #elif defined(AFS_XBSD_ENV) | |
518 | if (attrs->va_size != VNOVAL) { | |
519 | #elif defined(AFS_AIX41_ENV) | |
520 | if (attrs->va_size != -1) { | |
521 | #else | |
522 | if (attrs->va_size != ~0) { | |
523 | #endif | |
524 | if (!afs_AccessOK(avc, PRSFS_WRITE, treq, DONT_CHECK_MODE_BITS)) { | |
525 | code = EACCES; | |
526 | goto done; | |
527 | } | |
528 | } | |
529 | ||
530 | if (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW) { | |
531 | code = ENETDOWN; | |
532 | goto done; | |
533 | } | |
534 | ||
535 | afs_VAttrToAS(avc, attrs, &astat); /* interpret request */ | |
536 | code = 0; | |
537 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
538 | if (AFS_NFSXLATORREQ(acred)) { | |
539 | avc->execsOrWriters++; | |
540 | } | |
541 | #endif | |
542 | ||
543 | #if defined(AFS_SGI_ENV) | |
544 | AFS_RWLOCK((vnode_t *) avc, VRWLOCK_WRITE); | |
545 | #endif | |
546 | #if defined(AFS_DARWIN80_ENV) | |
547 | if (VATTR_IS_ACTIVE(attrs, va_data_size)) { | |
548 | #elif defined(AFS_LINUX22_ENV) || defined(UKERNEL) | |
549 | if (attrs->va_mask & ATTR_SIZE) { | |
550 | #elif defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
551 | if (attrs->va_mask & AT_SIZE) { | |
552 | #elif defined(AFS_XBSD_ENV) | |
553 | if (attrs->va_size != VNOVAL) { | |
554 | #elif defined(AFS_AIX41_ENV) | |
555 | if (attrs->va_size != -1) { | |
556 | #else | |
557 | if (attrs->va_size != ~0) { | |
558 | #endif | |
559 | afs_size_t tsize = attrs->va_size; | |
560 | ObtainWriteLock(&avc->lock, 128); | |
561 | avc->f.states |= CDirty; | |
562 | ||
563 | if (AFS_IS_DISCONNECTED && tsize >=avc->f.m.Length) { | |
564 | /* If we're growing the file, and we're disconnected, we need | |
565 | * to make the relevant dcache chunks appear ourselves. */ | |
566 | code = afs_ExtendSegments(avc, tsize, treq); | |
567 | } else { | |
568 | code = afs_TruncateAllSegments(avc, tsize, treq, acred); | |
569 | } | |
570 | #ifdef AFS_LINUX26_ENV | |
571 | /* We must update the Linux kernel's idea of file size as soon as | |
572 | * possible, to avoid racing with delayed writepages delivered by | |
573 | * pdflush */ | |
574 | if (code == 0) | |
575 | i_size_write(AFSTOV(avc), tsize); | |
576 | #endif | |
577 | #if defined(AFS_FBSD_ENV) || defined(AFS_DFBSD_ENV) | |
578 | vnode_pager_setsize(vp, (u_long) tsize); | |
579 | #endif | |
580 | /* if date not explicitly set by this call, set it ourselves, since we | |
581 | * changed the data */ | |
582 | if (!(astat.Mask & AFS_SETMODTIME)) { | |
583 | astat.Mask |= AFS_SETMODTIME; | |
584 | astat.ClientModTime = osi_Time(); | |
585 | } | |
586 | ||
587 | if (code == 0) { | |
588 | if (((avc->execsOrWriters <= 0) && (avc->f.states & CCreating) == 0) | |
589 | || (avc->execsOrWriters == 1 && AFS_NFSXLATORREQ(acred))) { | |
590 | ||
591 | /* Store files now if not disconnected. */ | |
592 | /* XXX: AFS_IS_DISCON_RW handled. */ | |
593 | if (!AFS_IS_DISCONNECTED) { | |
594 | code = afs_StoreAllSegments(avc, treq, AFS_ASYNC); | |
595 | if (!code) | |
596 | avc->f.states &= ~CDirty; | |
597 | } | |
598 | } | |
599 | } else | |
600 | avc->f.states &= ~CDirty; | |
601 | ||
602 | ReleaseWriteLock(&avc->lock); | |
603 | hzero(avc->flushDV); | |
604 | osi_FlushText(avc); /* do this after releasing all locks */ | |
605 | } | |
606 | ||
607 | if (!AFS_IS_DISCONNECTED) { | |
608 | if (code == 0) { | |
609 | ObtainSharedLock(&avc->lock, 16); /* lock entry */ | |
610 | code = afs_WriteVCache(avc, &astat, treq); /* send request */ | |
611 | ReleaseSharedLock(&avc->lock); /* release lock */ | |
612 | } | |
613 | if (code) { | |
614 | /* error? erase any changes we made to vcache entry */ | |
615 | afs_StaleVCache(avc); | |
616 | } | |
617 | } else { | |
618 | ObtainSharedLock(&avc->lock, 712); | |
619 | /* Write changes locally. */ | |
620 | code = afs_WriteVCacheDiscon(avc, &astat, attrs); | |
621 | ReleaseSharedLock(&avc->lock); | |
622 | } /* if (!AFS_IS_DISCONNECTED) */ | |
623 | ||
624 | #if defined(AFS_SUN5_ENV) || defined(AFS_SGI_ENV) | |
625 | if (AFS_NFSXLATORREQ(acred)) { | |
626 | avc->execsOrWriters--; | |
627 | } | |
628 | #endif | |
629 | #if defined(AFS_SGI_ENV) | |
630 | AFS_RWUNLOCK((vnode_t *) avc, VRWLOCK_WRITE); | |
631 | #endif | |
632 | done: | |
633 | afs_PutFakeStat(&fakestate); | |
634 | ||
635 | AFS_DISCON_UNLOCK(); | |
636 | code = afs_CheckCode(code, treq, 15); | |
637 | afs_DestroyReq(treq); | |
638 | return code; | |
639 | } | |
640 | ||
641 | /*! | |
642 | * Allocate a vattr. | |
643 | * | |
644 | * \note The caller must free the allocated vattr with | |
645 | * afs_DestroyAttr() if this function returns successfully (zero). | |
646 | * | |
647 | * \note The GLOCK must be held on platforms which require the GLOCK | |
648 | * for osi_AllocSmallSpace() and osi_FreeSmallSpace(). | |
649 | * | |
650 | * \param[out] out address of the vattr pointer | |
651 | * \return 0 on success | |
652 | */ | |
653 | int | |
654 | afs_CreateAttr(struct vattr **out) | |
655 | { | |
656 | struct vattr *vattr = NULL; | |
657 | ||
658 | if (!out) { | |
659 | return EINVAL; | |
660 | } | |
661 | vattr = osi_AllocSmallSpace(sizeof(struct vattr)); | |
662 | if (!vattr) { | |
663 | return ENOMEM; | |
664 | } | |
665 | memset(vattr, 0, sizeof(struct vattr)); | |
666 | *out = vattr; | |
667 | return 0; | |
668 | } | |
669 | ||
670 | /*! | |
671 | * Deallocate a vattr. | |
672 | * | |
673 | * \note The GLOCK must be held on platforms which require the GLOCK | |
674 | * for osi_FreeSmallSpace(). | |
675 | * | |
676 | * \param[in] vattr pointer to the vattr to free; may be NULL | |
677 | */ | |
678 | void | |
679 | afs_DestroyAttr(struct vattr *vattr) | |
680 | { | |
681 | if (vattr) { | |
682 | osi_FreeSmallSpace(vattr); | |
683 | } | |
684 | } | |
685 |