-
Notifications
You must be signed in to change notification settings - Fork 3
/
mvfs_clnt.c
2409 lines (2120 loc) · 74.3 KB
/
mvfs_clnt.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* * (C) Copyright IBM Corporation 1991, 2013. */
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Author: IBM Corporation
This module is part of the IBM (R) Rational (R) ClearCase (R)
Multi-version file system (MVFS).
For support, please visit http://www.ibm.com/software/support
*/
/* mvfs_clnt.c */
/*
* This is the MFS's client interface to the view server. It encapsulates
* the core of the calls in the view RPC trait. You will find
* "bindroot" in mfs_vnodeops.c and "get cleartext" in mfs_clearops.c
*
* WARNING: when you add new view server calls in here, you must make
* sure the trait descriptions (operation names and timeout
* shifts) are correct in mfs_utils.c or crashes may result.
*
* NOTE ON NAME CACHE:
* To promote consistency and avoid all those nice race conditions
* between an update to the view and the name cache contents,
* all operations that add/remove names
* from a dir should be single threaded with the name cache updates
* by an MLOCK/MUNLOCK pair on the dir vnode around both the mfscall
* to the view server, and the name cache add/remove op. The
* name cache add/remove ops REQUIRE (via assert) that the dir
* vnode is locked. This is not required for the name cache
* flushing ops.
*/
#include "mvfs_systm.h"
#include "mvfs.h"
#include <tbs_errno.h>
/* for dnc op prototypes */
#include "mvfs_dnc.h"
/*
* Some platforms have limited stack space and we need to save what we
* can. The RPC request/reply structures can be quite big on a 64-bit
* system, so we allocate them from the heap.
*/
#define HEAP_DEFINE(type,var) type *var
#define HEAP_ALLOC1(type,var) var = (type *)KMEM_ALLOC(sizeof(type),KM_SLEEP)
#define HEAP_ALLOC(type,var) \
HEAP_DEFINE(type,var); \
HEAP_ALLOC1(type,var);
#define HEAP_FREE(var) KMEM_FREE(var, sizeof(*var))
#define HEAP_ALLOC2(type1,var1,type2,var2) \
HEAP_DEFINE(type1,var1); \
HEAP_DEFINE(type2,var2); \
HEAP_ALLOC1(type1,var1); \
if (var1 == NULL) \
return ENOMEM; \
HEAP_ALLOC1(type2,var2); \
if (var2 == NULL) { \
HEAP_FREE(var1); \
return ENOMEM; \
}
/* MFS_CLNT_GETATTR_MNP - RPC to get attrs into mnode
* MNODE MUST BE LOCKED!
*/
int
mfs_clnt_getattr_mnp(
register mfs_mnode_t *mnp,
VFS_T *vfsp,
CALL_DATA_T *cd
)
{
int error;
VNODE_T *vw;
HEAP_ALLOC2(view_getattr_req_t,rap,view_getattr_reply_t,rrp);
ASSERT(MFS_ISVOB(mnp));
ASSERT(MISLOCKED(mnp));
vw = mnp->mn_hdr.viewvp;
if (vw == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ESRCH);
}
rap->hdr.view = VTOM(vw)->mn_view.vh;
rap->hdr.build_handle = MFS_BH(cd);
rap->fhandle = mnp->mn_vob.vfh;
error = mfs_vwcall(vw, vfsp, VIEW_GETATTR,
(xdrproc_t) xdr_view_getattr_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_getattr_reply_t, (caddr_t)rrp,
MVFS_CD2CRED(cd));
if (!error) error = mfs_geterrno(rrp->hdr.status);
if (!error) {
mnp->mn_vob.attr = rrp->vstat; /* Set stats */
/* Now convert user and group identities to native form
* and store in mnode
*/
MVFS_CREDUTL_SIDS_TO_NATIVE_IDS(mnp, MVFS_CD2CRED(cd));
mnp->mn_vob.lvut = rrp->lvut;
/*
* Fix up stats for a 'history mode' symlink.
* The size returned does not include the hm suffix
* we will add, because the view_server doesn't know
* it. So... we have to fix that up here.
*/
if (MFS_HMVFH(&rrp->fhandle) &&
mfs_ftype_to_vtype(mnp->mn_vob.attr.fstat.type) == VLNK)
{
mnp->mn_vob.attr.fstat.size += mfs_hmsuffix_len();
}
/* Set the attribute cache timeout */
mvfs_set_ac_timeout(mnp, vfsp, 0, TRUE, TRUE); /* Set timeout on stats */
/*
* To help out the FSS, fill in potentially misssing
* elem_dbid and generation number. See comments in mfs_iread()
*/
MFS_UPDATE_PARTIAL_VFH(mnp->mn_vob.vfh, rrp->fhandle);
} else if (error == ESTALE) {
/* Can't CHK_STALE w/o vnode ptr, so just log, flush all caches */
mvfs_logperr(MFS_LOG_ESTALE, error,
"getattr_mnp: vw=%s vob=%s dbid=0x%x",
mfs_vw2nm(vw), VFS_TO_MMI(vfsp)->mmi_mntpath,
mnp->mn_hdr.fid.mf_dbid);
mfs_dncflush(); /* includes an RVC flush */
}
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(error);
}
/* MFS_CLNT_GETATTR - RPC to the view to get attrs */
int
mfs_clnt_getattr(
register VNODE_T *vp,
CALL_DATA_T *cd
)
{
int error;
VNODE_T *vw;
mfs_mnode_t *mnp;
HEAP_ALLOC2(view_getattr_req_t,rap,view_getattr_reply_t,rrp);
mnp = VTOM(vp);
ASSERT(MFS_ISVOB(mnp));
vw = MFS_VIEW(vp);
if (vw == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ESRCH);
}
rap->hdr.view = VTOM(vw)->mn_view.vh;
rap->hdr.build_handle = MFS_BH(cd);
rap->fhandle = MFS_VFH(vp);
/*
* Must lock around getattr so that no races on attributes with
* dir ops. e.g. don't want go fetch attrs, then dir op gets
* and changes them/caches them, then I get the lock to update
* the attributes and put in old ones.
*/
MLOCK(mnp);
error = mfs_vwcall(vw, vp->v_vfsp, VIEW_GETATTR,
(xdrproc_t) xdr_view_getattr_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_getattr_reply_t, (caddr_t)rrp,
MVFS_CD2CRED(cd));
if (!error) error = mfs_geterrno(rrp->hdr.status);
if (!error) {
mnp->mn_vob.lvut = rrp->lvut;
#ifdef MVFS_DEBUG
if (!MFS_TVEQ(mnp->mn_vob.attr.fstat.ctime,
rrp->vstat.fstat.ctime))
MDB_XLOG((MDB_CTIME, "clnt_getattr updating ctime to 0x%"KS_FMT_TV_SEC_T_D".%"KS_FMT_TV_USEC_T_D"\n",
rrp->vstat.fstat.ctime.tv_sec, rrp->vstat.fstat.ctime.tv_usec));
#endif
/*
* Fix up stats for a 'history mode' symlink.
* The size returned does not include the hm suffix
* we will add, because the view_server doesn't know
* it. So... we have to fix that up here.
*/
if (MFS_HMVFH(&rrp->fhandle) &&
mfs_ftype_to_vtype(rrp->vstat.fstat.type) == VLNK)
{
rrp->vstat.fstat.size += mfs_hmsuffix_len();
}
(void) mfs_attrcache(vp, &rrp->vstat, 0, MVFS_CD2CRED(cd));
/* cache the LVUT too */
mvfs_set_ac_timeout(mnp, vp->v_vfsp, 0, TRUE, TRUE);
/*
* To help out the FSS, fill in potentially missing
* elem_dbid and generation number. See comments in mfs_iread()
*/
MFS_UPDATE_PARTIAL_VFH(mnp->mn_vob.vfh, rrp->fhandle);
} else {
MFS_CHK_STALE(error, vp)
}
MUNLOCK(mnp);
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(error);
}
/*
* MFS_CLNT_SETATTR
* Change only the view attributes (ignoring the cleartext).
* Used from mfs_inactive() to update info in the view only.
*
* Automatically merges in cleartext info and clears
* dirty/accessed bits if attributes successfully set.
*
* WARNING: vnode should already be locked for this call.
* the procedure name has the _locked at the end to remind
* you of this!
*/
int
mvfs_clnt_setattr_locked(
register VNODE_T *vp,
register VATTR_T *vap, /* WATCHOUT: may be null for just sync */
u_long xmode, /* extra (special) fmode bits */
int bhflag, /* Which build handle to use */
int wcred, /* We think cred is a "writer's" cred */
CALL_DATA_T *cd,
u_int saflag /* Set Attr Flag */
)
{
register mfs_mnode_t *mnp;
VNODE_T *vw;
int error;
u_long mask;
HEAP_ALLOC2(view_setattr_req_t,rap,view_setattr_reply_t,rrp);
vw = MFS_VIEW(vp);
if (vw == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ESRCH);
}
mnp = VTOM(vp);
ASSERT(MISLOCKED(mnp));
ASSERT(MFS_ISVOB(mnp));
/* Change attributes in the view database (if view object) */
/* Set up request */
rap->hdr.view = VTOM(vw)->mn_view.vh;
if (bhflag == MFS_USE_NULLBH) {
rap->hdr.build_handle = mfs_null_bh;
} else {
rap->hdr.build_handle = MFS_BH(cd);
}
rap->fhandle = MFS_VFH(vp);
/*
* If non-null attributes to set, then pre-process them and
* get mask bites telling us what fields are set.
*/
if (vap) {
mfs_vattr_to_sattr(vap, &rap->sattr);
mask = VATTR_GET_MASK(vap);
} else {
mfs_sattr_null(&rap->sattr);
mask = 0;
}
/* Merge in "audited" bit to setattr call */
if ((xmode == TBS_FMODE_AUDITED_OBJ) &&
(mnp->mn_vob.attr.fstat.mode & TBS_FMODE_AUDITED_OBJ) == 0) {
rap->sattr.mask |= VIEW_ATTR_AUDITED;
rap->sattr.mode |= TBS_FMODE_AUDITED_OBJ;
}
/* Merge in any modifications from cleartext with this
setattr call. Don't try to set mod time/size unless
we have writer credentials. */
if (mnp->mn_hdr.realvp) {
ASSERT(MVFS_ISVTYPE(vp, VREG));
if ( wcred ||
(MVFS_CD2CRED(cd) &&
(MVFS_COMPARE_MNODE_UID(MVFS_CD2CRED(cd), mnp->mn_vob.user_id))) )
{
if ((mask & AT_SIZE) == 0 &&
VATTR_GET_SIZE(&mnp->mn_vob.cleartext.va) !=
mnp->mn_vob.attr.fstat.size) {
rap->sattr.size = VATTR_GET_SIZE(&mnp->mn_vob.cleartext.va);
rap->sattr.mask |= VIEW_ATTR_SIZE;
}
/*
* When merging mtime, do this if the seconds are
* out of sync, or if we have made a modification
* that requires sync-ing the mtime since the last
* sync point (ctime counts too!), but not if the
* fstat is already correct to the tv_usec!
*/
if ((mask & AT_MTIME) == 0 &&
(VATTR_GET_MTIME(&mnp->mn_vob.cleartext.va) !=
mnp->mn_vob.attr.fstat.mtime.tv_sec ||
mnp->mn_vob.sync_mtime || mnp->mn_vob.sync_ctime)) {
VATTR_GET_MTIME_TV(&mnp->mn_vob.cleartext.va, &rap->sattr.mtime);
/* Only set mtime if it would be different */
if (!MFS_TVEQ(mnp->mn_vob.attr.fstat.mtime, rap->sattr.mtime))
rap->sattr.mask |= VIEW_ATTR_MTIME;
else mnp->mn_vob.sync_mtime = 0; /* False alarm */
}
}
/*
* Only update atime with lo-res check,
* but set hi-res time if available from cleartext.
*
* Some FS's (notably FAT on NT) do not support the Atime
* parameter. The definition for wrappers on these systems
* is that they should return a 0 as the atime if the underlying
* system does not keep atime. Here in this code, we then ignore
* trying to 'sync' the atime to the view-server (leaving a useless
* atime in the view, but better than 0 would be).
*/
if ((mask & AT_ATIME) == 0 &&
VATTR_GET_ATIME(&mnp->mn_vob.cleartext.va) !=
mnp->mn_vob.attr.fstat.atime.tv_sec &&
VATTR_GET_ATIME(&mnp->mn_vob.cleartext.va) != 0) {
VATTR_GET_ATIME_TV(&mnp->mn_vob.cleartext.va, &rap->sattr.atime);
rap->sattr.mask |= VIEW_ATTR_ATIME;
}
}
/* Don't do call if nothing to update. */
if (mfs_sattr_is_null(&rap->sattr)) {
MDB_XLOG((MDB_CTIME, "clnt_setattr no work wcred=%d\n", wcred));
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(0);
} else
MDB_XLOG((MDB_CTIME, "clnt_setattr mask=%x\n", rap->sattr.mask));
#ifdef MVFS_DEBUG
/* Print warning if either time is 0 */
if (rap->sattr.mtime.tv_sec == 0 || rap->sattr.atime.tv_sec == 0) {
mvfs_log(MFS_LOG_DEBUG,
"setattr: setting a/mtime of 0: vw=%s vob=%s dbid=0x%x\n",
mfs_vp2vw(vp), mfs_vp2dev(vp), mfs_vp2dbid(vp));
}
/* Print warning if setting size of non-reg file */
if ((rap->sattr.mask & VIEW_ATTR_SIZE) && !MVFS_ISVTYPE(vp, VREG)) {
mvfs_log(MFS_LOG_DEBUG,
"setattr: setting size of non-VREG: vw=%s vob=%s dbid=0x%x\n",
mfs_vp2vw(vp), mfs_vp2dev(vp), mfs_vp2dbid(vp));
}
#endif
error = mfs_vwcall(vw, vp->v_vfsp, VIEW_SETATTR,
(xdrproc_t) xdr_view_setattr_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_setattr_reply_t, (caddr_t)rrp,
MVFS_CD2CRED(cd));
if (!error) {
/*
* If the flag bit MVFS_SATTR_ATIME_EROFS_OK is set, we don't want to see EROFS
* when we fail to sync last-access-time from cleartext to viewdb on a ReadOnly
* view.
*
* RATLC01011420: When the view server returns TBS_ST_EROFS then rrp->vstat
* is not populated. Thereby there is nothing to update the attribute
* cache with. If we still go ahead with the mfs_attrcache call,
* we fail on the debug assertion, sid_p->type == CREDUTL_SID_TYPE_UNIX,
* in credutl_sid_to_unix_uid() and credutl_sid_to_unix_gid()
* which get called down the order.
* See the CR for details.
*/
if ((saflag & MVFS_SATTR_ATIME_EROFS_OK) != 0
&& (rap->sattr.mask == VIEW_ATTR_ATIME)
&& (rrp->hdr.status == TBS_ST_EROFS))
{
MDB_XLOG((MDB_CTIME, "clnt_setattr: read-only view: nothing to do (mcred=%"KS_FMT_PTR_T")\n",
MCRED(mnp)));
return(0);
}
error = mfs_geterrno(rrp->hdr.status);
}
if (!error) {
#ifdef MVFS_DEBUG
if (!MFS_TVEQ(mnp->mn_vob.attr.fstat.ctime,
rrp->vstat.fstat.ctime))
MDB_XLOG((MDB_CTIME,
"clnt_setattr updating ctime to 0x%"KS_FMT_TV_SEC_T_D".%"KS_FMT_TV_USEC_T_D" wcred=%d\n",
rrp->vstat.fstat.ctime.tv_sec,
rrp->vstat.fstat.ctime.tv_usec, wcred));
else
MDB_XLOG((MDB_CTIME,
"clnt_setattr nodelta ctime 0x%"KS_FMT_TV_SEC_T_D".%"KS_FMT_TV_USEC_T_D" wcred=%d\n",
rrp->vstat.fstat.ctime.tv_sec,
rrp->vstat.fstat.ctime.tv_usec, wcred));
#endif
(void) mfs_attrcache(vp, &rrp->vstat, 0, MVFS_CD2CRED(cd));
/* Clear bits if we have sync'd (or set) the mtime */
if (rap->sattr.mask != 0)
/* any change will have set the ctime in the reply */
mnp->mn_vob.sync_ctime = 0;
if (rap->sattr.mask & VIEW_ATTR_MTIME) mnp->mn_vob.sync_mtime = 0;
} else {
MFS_CHK_STALE(error, vp);
MDB_XLOG((MDB_CTIME, "clnt_setattr error %d", error));
}
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(error);
}
/*
* MFS_CLNT_READLINK - enter with link text buffer allocated.
* Must be MAXPATHLEN in size.
*/
int
mfs_clnt_readlink(
register VNODE_T *vp,
mfs_pn_char_t *lnkbuf,
int *lnklenp,
CALL_DATA_T *cd
)
{
VNODE_T *vw;
int error;
HEAP_ALLOC2(view_readlink_req_t,rap,view_readlink_reply_t,rrp);
ASSERT(MFS_ISVOB(VTOM(vp)));
ASSERT(lnkbuf);
/* Check for view. If have none, then bug out. */
vw = MFS_VIEW(vp);
if (vw == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ESRCH);
}
rap->hdr.view = VTOM(vw)->mn_view.vh;
rap->hdr.build_handle = MFS_BH(cd);
rap->fhandle = MFS_VFH(vp);
rap->max_text_size = MAXPATHLEN;
rrp->text = lnkbuf;
error = mfs_vwcall(vw, vp->v_vfsp, VIEW_READLINK,
(xdrproc_t) xdr_view_readlink_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_readlink_reply_t, (caddr_t)rrp,
MVFS_CD2CRED(cd));
if (!error) error = mfs_geterrno(rrp->hdr.status);
if (!error) {
*lnklenp = (int)rrp->text_size;
/* To follow symlinks when warping in history mode,
append a HM suffix to the text of any history mode symlink. */
if (MFS_HMVFH(&rap->fhandle)) {
*lnklenp = *lnklenp + mfs_hmstrcat(lnkbuf);
}
} else {
MFS_CHK_STALE(error, vp);
}
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(error);
}
/*
* MFS_CLNT_REMOVE
*/
int
mfs_clnt_remove(
register VNODE_T *dvp,
mfs_pn_char_t *nm,
int bhflag,
int sleep,
CALL_DATA_T *cd
)
{
int error;
VNODE_T *vw;
mfs_mnode_t *mnp;
struct timeval dtm_save;
HEAP_ALLOC2(view_remove_req_t,rap,view_remove_reply_t,rrp);
vw = MFS_VIEW(dvp);
if (vw == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ESRCH);
}
if (mfs_hmname(nm, NULL)) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(EROFS);
}
rap->hdr.view = VTOM(vw)->mn_view.vh;
if (bhflag == MFS_USE_NULLBH) {
rap->hdr.build_handle = mfs_null_bh;
} else {
rap->hdr.build_handle = MFS_BH(cd);
}
rap->file.d_fhandle = MFS_VFH(dvp);
rap->file.name = nm;
mnp = VTOM(dvp);
if (sleep) {
MLOCK(mnp);
} else {
if (!MLOCK_NOWAIT(mnp)) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return EAGAIN;
}
}
dtm_save = rap->dir_dtm = mnp->mn_vob.attr.fstat.mtime;
mfs_dncremove(dvp, nm, MVFS_CD2CRED(cd)); /* Remove NC ent before call to view */
error = mfs_vwcall(vw, dvp->v_vfsp, VIEW_REMOVE,
(xdrproc_t) xdr_view_remove_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_remove_reply_t, (caddr_t)rrp,
MVFS_CD2CRED(cd));
if (!error) error = mfs_geterrno(rrp->hdr.status);
if (!error) {
(void) mfs_attrcache(dvp, &rrp->dir_mod.dvstat,
rrp->dir_mod.dir_dtm_valid, MVFS_CD2CRED(cd));
/*
* When the directory modification time stamp changes due to the view
* RPC, rddir cache would normally get flushed as a side effect of
* mfs_attrcache() call, via mfs_ac_modevents()->mvfs_rddir_cache_flush().
* As remove vnodeop renders the parent's rddir cache stale it is
* flushed here explicitly if mfs_attrcache() has not taken care of it.
* If rddir cache for this directory mnode has already been flushed due
* to the side effect mentioned above, then all the blocks including the
* first one are marked invalid and call to mvfs_rddir_cache_flush() is
* unnecessary.
*/
if (mnp->mn_vob.rddir_cache &&
(mnp->mn_vob.rddir_cache->entries[0]).valid) {
mvfs_log(MFS_LOG_DEBUG,
"remove:flushing rddir explicitly:vp=%"KS_FMT_PTR_T
", nm=%s; mtime: before view RPC=%"KS_FMT_TV_SEC_T_X
".%"KS_FMT_TV_USEC_T_X" after view RPC=0x%"KS_FMT_TV_SEC_T_X
".%"KS_FMT_TV_USEC_T_X"\n", dvp, nm, dtm_save.tv_sec,
dtm_save.tv_usec, rrp->dir_mod.dvstat.fstat.mtime.tv_sec,
rrp->dir_mod.dvstat.fstat.mtime.tv_usec);
mvfs_rddir_cache_flush(mnp);
}
} else {
MFS_CHK_STALE(error, dvp);
}
MUNLOCK(mnp);
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(error);
}
/*
* MFS_CLNT_LOOKUP - call view for lookup op
*/
int
mfs_clnt_lookup(
VNODE_T *dvp,
mfs_pn_char_t *nm,
VNODE_T **vpp,
CALL_DATA_T *cd
)
{
VNODE_T *vw;
mfs_mnode_t *mnp;
view_hm_warp_opt_t hm_warp_opt = VIEW_HM_WARP_OPT_NONE;
int error;
int hm, hmwarp;
struct timeval mtime;
int isdotdot = 0;
int pri;
u_int dncflags;
int view_op = VIEW_LOOKUP;
HEAP_ALLOC2(view_lookup_req_t,rap,view_lookup_reply_t,rrp);
vw = MFS_VIEW(dvp);
if (vw == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ESRCH);
}
MVFS_INIT_TIMEVAL(mtime);
/*
* Look for history mode warp names and handle as follows:
* Cases are:
*
* View Name (hm suffix?) Action
* ------ ---------------- ------------------------
* Not HM Not HM suffix Normal lookup on name
* HM suffix Warp into history mode and
* lookup name with suffix
* stripped
* HM Not HM suffix Normal lookup on name
* HM suffix Lookup name with suffix
* stripped (stay in HM)
* Also: look out for special cases:
*
* Case "." or ".@@": If lookup same as parent, mfs_makevobnode would
* try to lock an already locked vnode and panic on a recursive lock.
*
* View Name (hm suffix?) Action
* ------ ---------------- ------------------------
* Not HM "." Return same vnode
* ".@@" Real lookup required for warp.
* Return vnode is in a
* in a different view/tree.
* HM "." Return same vnode
* ".@@" Return same vnode
*
* Case ".." or "..@@": Lookup is going up the tree. If kept dir
* vnode locked while in mfs_makevobnode (and lock result of lookup),
* then lock order would be child->parent and can cause a deadlock.
* So... must unlock the dir before the mfs_makevobnode below.
*
* Case: "^@@" (hmvers_nm) is really a lookup of ".@@" but
* it warps to the version level instead of the element level
* of the current directory. (Generally this really means a lookup
* of "."!!)
*/
hm = mfs_hmname(nm, &rap->name); /* Get "stripped" name in rap->name */
if (hm) {
hm_warp_opt = VIEW_HM_WARP_OPT_ELEMENT_WARP; /* Warp to element */
/* rap->name already has stripped hm name */
} else {
hm_warp_opt = VIEW_HM_WARP_OPT_NONE; /* No warp */
rap->name = nm;
}
/*
* Check for HM warp to version e.g. handling of "^@@"
*/
if (hm && rap->name[0] == MVFS_VX_VERS_CHAR && rap->name[1] == '\0') {
/* Have found ^@@ in non-hm mode. Rewrite the name lookup
* to be ".", and the warp mode to be to the VERSION instead
* of the element. Ok to rewrite the string because
* for HM we know that "rap->name" points to a locally
* allocated copy, not the passed in argument.
*/
ASSERT(rap->name != nm);
rap->name[0] = '.';
hm_warp_opt = VIEW_HM_WARP_OPT_VERSION_WARP;
}
/*
* Check for various cases.
* Optimize by only doing this if first char is "."
*/
if (rap->name[0] == '.') {
/* Check for "." cases */
if (rap->name[1] == '\0') {
if (!hm) { /* Leafname not History Mode i.e. just "." */
*vpp = dvp;
VN_HOLD(dvp);
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(0);
} else if (VTOM(vw)->mn_view.hm) { /* ".@@" when already in HM */
*vpp = dvp;
VN_HOLD(dvp);
STRFREE(rap->name);
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(0);
}
}
/*
* Set flag if ".." - this is used later to
* control locking order.
*/
isdotdot = (rap->name[1] == '.' && rap->name[2] == '\0');
}
rap->hdr.view = VTOM(vw)->mn_view.vh;
rap->hdr.build_handle = MFS_BH(cd);
rap->d_fhandle = MFS_VFH(dvp);
rap->hm_warp_opt = hm_warp_opt;
rap->residual_pname = ""; /* FIXME: Residual pathname, none for now ... */
/* RATLC01031475: Downrev view support. For 7.1 clients to connect
* to 6.0 view_servers, the view_lookup RPC is the key difference.
* This change is accompanied by changes in libatriaview to allow setview
* command to succeed.
*/
if (VTOM(vw)->mn_view.downrev_view)
view_op = VIEW_LOOKUP_V6;
mnp=VTOM(dvp);
MLOCK(mnp);
error = mfs_vwcall(vw, dvp->v_vfsp, view_op,
(xdrproc_t) xdr_view_lookup_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_lookup_reply_t, (caddr_t)rrp, MVFS_CD2CRED(cd));
/* Downrev view support: RPC error that comes back from the unknown
* RPC varies a bit between platforms.
*/
if (error) mvfs_log(MFS_LOG_DEBUG,"view_lookup RPC mismatch %s\n",mfs_strerr(error));
if (error == EIO) {
/* Expect this error on the first lookup to a v6 view, and try v6
* lookup. If it succeeds, set flag so subsequent calls will succeed
* on their first RPC. But if this also fails, treat as a true error.
*/
error = mfs_vwcall(vw, dvp->v_vfsp, VIEW_LOOKUP_V6,
(xdrproc_t) xdr_view_lookup_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_lookup_reply_t, (caddr_t)rrp,
MVFS_CD2CRED(cd));
/* Error or not, fall through to normal processing. */
if (!error) {
VTOM(vw)->mn_view.downrev_view = 1;
mvfs_log(MFS_LOG_INFO, "Marking v6 view %s\n", mfs_vw2nm(vw));
}
}
if (hm) {
ASSERT(rap->name != nm);
STRFREE(rap->name); /* Free allocated stripped name */
}
if (!error) error = mfs_geterrno(rrp->hdr.status);
if (!error) {
/*
* Check for accidental lookup of "." by another name
* If so, handle this and print a debug warning to the
* console. The idea is to be very ROBUST, cause anything
* could get cataloged in the namespace.
*/
if (mnp->mn_vob.vfh.ver_dbid == rrp->fhandle.ver_dbid &&
mnp->mn_vob.vfh.gen == rrp->fhandle.gen &&
((mnp->mn_vob.vfh.flags ^ rrp->fhandle.flags) &
VIEW_FHANDLE_FLAGS_HISTORY_MODE) == 0 &&
BCMP(&mnp->mn_vob.vfh.vob_uuid, &rrp->fhandle.vob_uuid, sizeof(tbs_uuid_t)) == 0) {
*vpp = dvp;
VN_HOLD(dvp);
mfs_dncadd(dvp,
rrp->bh_invariant ? MFS_DNC_BHINVARIANT : 0,
nm, *vpp, MVFS_CD2CRED(cd));
MUNLOCK(mnp);
mvfs_log(MFS_LOG_INFO,
"vw lookup: vw=%s vob=%s dbid=0x%x nm=%s hardlink to '.'!\n",
mfs_vp2vw(dvp),
mfs_vp2dev(dvp), mfs_vp2dbid(dvp), nm);
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(0);
}
/*
* Lookup is the only clnt op which can do a makevobnode
* on its parent (when going "up" the tree). This can
* cause a deadlock when:
* Proc A Proc B
* MLOCK dir <parent> MLOCK dir <child>
* RPC lookup <child> RPC lookup ".."
* Makevobnode <child> Makevobnode ".."
* MLOCK <child> waits! MLOCK <parent> waits!
*
* (No other op e.g. create, mkdir etc. can makevobnode a parent.)
*
* SO, when going up the tree, use the mod time of the dir to see if
* anything might have changed between when we did the lookup RPC, and
* when we try to add the name cache entry. If anything might
* have changed, then don't add the name cache entry (i.e.
* if all attributes invalidated on the dir and not refetched
* yet, or the mtime changed).
*
* I don't do this for all lookups, because I would lose too
* many name cache entries that way during normal operation of
* builds etc. (Note that a dir mod is not required, just anything
* that invalidates the attributes e.g. like and open!). Also,
* holding the dir locked across the RPC is not a big issue because
* it really doesn't matter all that much whether we are single
* threaded in the MFS, or at the view_server (which only handles 1
* RPC at a time!), and the value of the name cache to performance
* far outweighs anything else.
*/
if (isdotdot) {
mtime = mnp->mn_vob.attr.fstat.mtime;
MUNLOCK(mnp);
}
/*
* Fix up stats for a 'history mode' symlink.
* The size returned does not include the hm suffix
* we will add, because the view_server doesn't know
* it. So... we have to fix that up here.
*/
if (MFS_HMVFH(&rrp->fhandle) &&
mfs_ftype_to_vtype(rrp->vstat.fstat.type) == VLNK)
{
rrp->vstat.fstat.size += mfs_hmsuffix_len();
}
error = mfs_makevobnode(&rrp->vstat, &rrp->lvut, vw, &rrp->fhandle,
dvp->v_vfsp, MVFS_CD2CRED(cd), vpp);
if (isdotdot) {
MLOCK(mnp); /* Must relock the dir */
if (!error) {
/* Only add to name cache if noone has invalidated the
* attributes and mtime has not changed. For this
* check I don't care about timed-out attributes, only
* if a process on this node explicitly invalidated them
* (e.g. due to a dir op).
*/
if (MFS_ATTRISVALID(dvp) &&
MFS_TVEQ(mtime, mnp->mn_vob.attr.fstat.mtime)) {
mfs_dncadd(dvp,
rrp->bh_invariant ? MFS_DNC_BHINVARIANT : 0,
nm, *vpp, MVFS_CD2CRED(cd));
} else {
/* Count this as an add race. */
BUMPSTAT(mfs_dncstat.dnc_addunlock);
BUMPVSTATV(vw,dncstat.dnc_addunlock);
}
}
} else { /* Normal lookups */
if (!error) {
mfs_dncadd(dvp,
rrp->bh_invariant ? MFS_DNC_BHINVARIANT : 0,
nm, *vpp, MVFS_CD2CRED(cd));
switch (MVFS_GETVTYPE(*vpp)) {
case VDIR:
BUMPSTAT(mfs_dncstat.dnc_missdir);
BUMPVSTATV(vw,dncstat.dnc_missdir);
break;
case VREG:
BUMPSTAT(mfs_dncstat.dnc_missreg);
BUMPVSTATV(vw,dncstat.dnc_missreg);
break;
default:
break;
}
}
}
} else if (error == ENOENT) {
/*
* Cache "name not found"
* FIXME: For now (for safety) these are NEVER bh invariant
* for VOB dirs (view didn't send rrp->bh_invariant in reply
* because status was not OK). However, we can always make
* a translation BH_INVARIANT for a view-pvt dir, since ref
* time can't affect the result of view-pvt dir translations.
*/
if (VIEW_ISA_VIEW_OBJ(&mnp->mn_vob.vfh))
dncflags = MFS_DNC_BHINVARIANT;
else
dncflags = 0;
/*
* If the reason this is not visible is because it's not there,
* then we can cache the result specially and avoid some inefficient
* invalidations. See mfs_dncops.c.
*/
if ((rrp->name_state & VIEW_NAME_STATE_NOT_VISIBLE) ==
VIEW_NAME_STATE_ENOTENT)
dncflags |= MFS_DNC_NOTINDIR;
mfs_dncadd(dvp, dncflags, nm, NULL, MVFS_CD2CRED(cd));
BUMPSTAT(mfs_dncstat.dnc_missnoent);
BUMPVSTATV(vw,dncstat.dnc_missnoent);
/*
* Log ENOENT lookup if debug, or at DEBUG level if
* the underlying error is "no version selected"
*/
if (rrp->hdr.status != TBS_ST_VIEW_NO_VER) {
mvfs_logperr(MFS_LOG_ENOENT, ENOENT,
"vw lookup: vw=%s vob=%s dbid=0x%x nm=%s",
mfs_vp2vw(dvp), mfs_vp2dev(dvp), mfs_vp2dbid(dvp), nm);
} else {
mvfs_log(MFS_LOG_ENOENT, "lookup: vw=%s vob=%s dbid=0x%x nm=%s - no version selected\n",
mfs_vp2vw(dvp), mfs_vp2dev(dvp), mfs_vp2dbid(dvp), nm);
}
} else {
MFS_CHK_STALE(error, dvp);
}
MUNLOCK(mnp);
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(error);
}
/*
* MFS_CLNT_CREATE - do op to view server to create
*/
int
mfs_clnt_create(
VNODE_T *dvp,
mfs_pn_char_t *nm,
VATTR_T *va,
VNODE_T **vpp,
CALL_DATA_T *cd
)
{
int error;
int xerr;
VNODE_T *vw;
mfs_mnode_t *mnp;
struct timeval dtm_save;
HEAP_ALLOC2(view_create_req_t,rap,view_create_reply_t,rrp);
ASSERT(MFS_ISVOB(VTOM(dvp)));
vw = MFS_VIEW(dvp);
if (vw == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ESRCH);
}
if (mfs_hmname(nm, NULL)) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(EROFS); /* History mode names an error */
}
rap->hdr.view = VTOM(vw)->mn_view.vh;
rap->hdr.build_handle = MFS_BH(cd);
rap->create.d_fhandle = MFS_VFH(dvp);
rap->create.name = nm;
mfs_vattr_to_sattr(va, &rap->iattr);
rrp->text = KMEM_ALLOC(MAXPATHLEN, KM_SLEEP);
if (rrp->text == NULL) {
HEAP_FREE(rap);
HEAP_FREE(rrp);
return(ENOMEM);
}
rap->max_text_size = MAXPATHLEN;
mnp = VTOM(dvp);
MLOCK(mnp);
dtm_save = rap->dir_dtm = mnp->mn_vob.attr.fstat.mtime;
error = mfs_vwcall(vw, dvp->v_vfsp, VIEW_CREATE,
(xdrproc_t) xdr_view_create_req_t, (caddr_t)rap,
(xdrproc_t) xdr_view_create_reply_t, (caddr_t)rrp, MVFS_CD2CRED(cd));
MFS_ATTRINVAL(dvp);
if (!error) error = mfs_geterrno(rrp->hdr.status);
if (!error) {