xref: /plan9/sys/src/cmd/dossrv/xfile.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include "iotrack.h"
4 #include "dat.h"
5 #include "fns.h"
6 
7 #define	FIDMOD	127	/* prime */
8 
9 static Xfs	*xhead;
10 static Xfile	*xfiles[FIDMOD], *freelist;
11 static MLock	xlock, xlocks[FIDMOD], freelock;
12 
13 static int
okmode(int omode,int fmode)14 okmode(int omode, int fmode)
15 {
16 	if(omode == OREAD)
17 		return fmode & 4;
18 	/* else ORDWR */
19 	return (fmode & 6) == 6;
20 }
21 
22 Xfs *
getxfs(char * user,char * name)23 getxfs(char *user, char *name)
24 {
25 	Xfs *xf, *fxf;
26 	Dir *dir;
27 	Qid dqid;
28 	char *p, *q;
29 	long offset;
30 	int fd, omode;
31 
32 	USED(user);
33 	if(name==nil || name[0]==0)
34 		name = deffile;
35 	if(name == nil){
36 		errno = Enofilsys;
37 		return 0;
38 	}
39 
40 	/*
41 	 * If the name passed is of the form 'name:offset' then
42 	 * offset is used to prime xf->offset. This allows accessing
43 	 * a FAT-based filesystem anywhere within a partition.
44 	 * Typical use would be to mount a filesystem in the presence
45 	 * of a boot manager programme at the beginning of the disc.
46 	 */
47 	offset = 0;
48 	if(p = strrchr(name, ':')){
49 		*p++ = 0;
50 		offset = strtol(p, &q, 0);
51 		chat("name %s, offset %ld\n", p, offset);
52 		if(offset < 0 || p == q){
53 			errno = Enofilsys;
54 			return 0;
55 		}
56 		offset *= Sectorsize;
57 	}
58 
59 	if(readonly)
60 		omode = OREAD;
61 	else
62 		omode = ORDWR;
63 	fd = open(name, omode);
64 
65 	if(fd < 0 && omode==ORDWR){
66 		omode = OREAD;
67 		fd = open(name, omode);
68 	}
69 
70 	if(fd < 0){
71 		chat("can't open %s: %r\n", name);
72 		errno = Eerrstr;
73 		return 0;
74 	}
75 	dir = dirfstat(fd);
76 	if(dir == nil){
77 		errno = Eio;
78 		close(fd);
79 		return 0;
80 	}
81 
82 	dqid = dir->qid;
83 	free(dir);
84 	mlock(&xlock);
85 	for(fxf=0,xf=xhead; xf; xf=xf->next){
86 		if(xf->ref == 0){
87 			if(fxf == 0)
88 				fxf = xf;
89 			continue;
90 		}
91 		if(!eqqid(xf->qid, dqid))
92 			continue;
93 		if(strcmp(xf->name, name) != 0 || xf->dev < 0)
94 			continue;
95 		if(devcheck(xf) < 0) /* look for media change */
96 			continue;
97 		if(offset && xf->offset != offset)
98 			continue;
99 		chat("incref \"%s\", dev=%d...", xf->name, xf->dev);
100 		++xf->ref;
101 		unmlock(&xlock);
102 		close(fd);
103 		return xf;
104 	}
105 	if(fxf == nil){
106 		fxf = malloc(sizeof(Xfs));
107 		if(fxf == nil){
108 			unmlock(&xlock);
109 			close(fd);
110 			errno = Enomem;
111 			return nil;
112 		}
113 		fxf->next = xhead;
114 		xhead = fxf;
115 	}
116 	chat("alloc \"%s\", dev=%d...", name, fd);
117 	fxf->name = strdup(name);
118 	fxf->ref = 1;
119 	fxf->qid = dqid;
120 	fxf->dev = fd;
121 	fxf->fmt = 0;
122 	fxf->offset = offset;
123 	fxf->ptr = nil;
124 	fxf->isfat32 = 0;
125 	fxf->omode = omode;
126 	unmlock(&xlock);
127 	return fxf;
128 }
129 
130 void
refxfs(Xfs * xf,int delta)131 refxfs(Xfs *xf, int delta)
132 {
133 	mlock(&xlock);
134 	xf->ref += delta;
135 	if(xf->ref == 0){
136 		chat("free \"%s\", dev=%d...", xf->name, xf->dev);
137 		free(xf->name);
138 		free(xf->ptr);
139 		purgebuf(xf);
140 		if(xf->dev >= 0){
141 			close(xf->dev);
142 			xf->dev = -1;
143 		}
144 	}
145 	unmlock(&xlock);
146 }
147 
148 Xfile *
xfile(int fid,int flag)149 xfile(int fid, int flag)
150 {
151 	Xfile **hp, *f, *pf;
152 	int k;
153 
154 	k = ((ulong)fid) % FIDMOD;
155 	hp = &xfiles[k];
156 	mlock(&xlocks[k]);
157 	pf = nil;
158 	for(f=*hp; f; f=f->next){
159 		if(f->fid == fid)
160 			break;
161 		pf = f;
162 	}
163 	if(f && pf){
164 		pf->next = f->next;
165 		f->next = *hp;
166 		*hp = f;
167 	}
168 	switch(flag){
169 	default:
170 		panic("xfile");
171 	case Asis:
172 		unmlock(&xlocks[k]);
173 		return (f && f->xf && f->xf->dev < 0) ? nil : f;
174 	case Clean:
175 		break;
176 	case Clunk:
177 		if(f){
178 			*hp = f->next;
179 			unmlock(&xlocks[k]);
180 			clean(f);
181 			mlock(&freelock);
182 			f->next = freelist;
183 			freelist = f;
184 			unmlock(&freelock);
185 		} else
186 			unmlock(&xlocks[k]);
187 		return nil;
188 	}
189 	unmlock(&xlocks[k]);
190 	if(f)
191 		return clean(f);
192 	mlock(&freelock);
193 	if(f = freelist){	/* assign = */
194 		freelist = f->next;
195 		unmlock(&freelock);
196 	} else {
197 		unmlock(&freelock);
198 		f = malloc(sizeof(Xfile));
199 		if(f == nil){
200 			errno = Enomem;
201 			return nil;
202 		}
203 	}
204 	mlock(&xlocks[k]);
205 	f->next = *hp;
206 	*hp = f;
207 	unmlock(&xlocks[k]);
208 	f->fid = fid;
209 	f->flags = 0;
210 	f->qid = (Qid){0,0,0};
211 	f->xf = nil;
212 	f->ptr = nil;
213 	return f;
214 }
215 
216 Xfile *
clean(Xfile * f)217 clean(Xfile *f)
218 {
219 	if(f->ptr){
220 		free(f->ptr);
221 		f->ptr = nil;
222 	}
223 	if(f->xf){
224 		refxfs(f->xf, -1);
225 		f->xf = nil;
226 	}
227 	f->flags = 0;
228 	f->qid = (Qid){0,0,0};
229 	return f;
230 }
231 
232 /*
233  * the file at <addr, offset> has moved
234  * relocate the dos entries of all fids in the same file
235  */
236 void
dosptrreloc(Xfile * f,Dosptr * dp,ulong addr,ulong offset)237 dosptrreloc(Xfile *f, Dosptr *dp, ulong addr, ulong offset)
238 {
239 	int i;
240 	Xfile *p;
241 	Dosptr *xdp;
242 
243 	for(i=0; i < FIDMOD; i++){
244 		for(p = xfiles[i]; p != nil; p = p->next){
245 			xdp = p->ptr;
246 			if(p != f && p->xf == f->xf
247 			&& xdp != nil && xdp->addr == addr && xdp->offset == offset){
248 				memmove(xdp, dp, sizeof(Dosptr));
249 				xdp->p = nil;
250 				xdp->d = nil;
251 				p->qid.path = QIDPATH(xdp);
252 			}
253 		}
254 	}
255 }
256