xref: /plan9/sys/src/cmd/aquarela/smbcomwrite.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include "headers.h"
2 
3 #define INMEMORYTRUNCTHRESH (256 * 1024)
4 
5 static int
6 dirfwstatlength(int fd, vlong offset)
7 {
8 	Dir d;
9 	memset(&d, 0xff, sizeof(d));
10 	d.name = d.uid = d.gid = d.muid = nil;
11 	d.length = offset;
12 	return dirfwstat(fd, &d);
13 }
14 
15 SmbProcessResult
16 smbtruncatefile(SmbSession *s, SmbFile *f, vlong offset)
17 {
18 	Dir *d;
19 	ulong o;
20 	uchar *db = nil;
21 	vlong length;
22 	int rv;
23 	SmbProcessResult pr;
24 
25 	d = dirfstat(f->fd);
26 	assert(d);
27 	length = d->length;
28 	free(d);
29 
30 	if (length == offset)
31 		return SmbProcessResultReply;
32 
33 	rv = dirfwstatlength(f->fd, offset);
34 	if (rv == 0) {
35 		pr = SmbProcessResultReply;
36 		goto done;
37 	}
38 //smblogprint(-1, "dirfwstatlength failed: %r\n");
39 	if (length > offset) {
40 		int nfd;
41 		char *fullpath;
42 		if (offset > INMEMORYTRUNCTHRESH) {
43 			smblogprint(-1, "smbcomwrite: truncation beyond %lud not supported\n", offset);
44 			pr = SmbProcessResultUnimp;
45 			goto done;
46 		}
47 		db = smbemalloc(offset);
48 		if (pread(f->fd, db, offset, 0) != offset) {
49 			pr = SmbProcessResultMisc;
50 			goto done;
51 		}
52 		fullpath = nil;
53 		smbstringprint(&fullpath, "%s%s", f->t->serv->path, f->name);
54 		nfd = open(fullpath, f->p9mode | OTRUNC);
55 		free(fullpath);
56 		if (nfd < 0) {
57 			smbseterror(s, ERRDOS, ERRnoaccess);
58 			pr = SmbProcessResultError;
59 			goto done;
60 		}
61 		close(nfd);
62 		if (pwrite(f->fd, db, offset, 0) != offset) {
63 			pr = SmbProcessResultMisc;
64 			goto done;
65 		}
66 		pr = SmbProcessResultReply;
67 	}
68 	else {
69 		db = smbemalloc(16384);
70 		memset(db, 0, 16384);
71 		o = length;
72 		while (o < offset) {
73 			long tt = 16384;
74 			if (tt > offset - o)
75 				tt = offset - o;
76 			if (pwrite(f->fd, db, tt, o) != tt) {
77 				smbseterror(s, ERRDOS, ERRnoaccess);
78 				pr = SmbProcessResultError;
79 				goto done;
80 			}
81 			o += tt;
82 		}
83 		pr = SmbProcessResultReply;
84 	}
85 done:
86 	free(db);
87 	return pr;
88 }
89 
90 SmbProcessResult
91 smbcomwrite(SmbSession *s, SmbHeader *h, uchar *pdata, SmbBuffer *b)
92 {
93 	SmbTree *t;
94 	SmbFile *f;
95 	ushort fid;
96 	ushort count;
97 	ulong offset;
98 	long nb;
99 	ushort yacount;
100 	uchar fmt;
101 
102 	if (h->wordcount != 5)
103 		return SmbProcessResultFormat;
104 
105 	fid = smbnhgets(pdata); pdata += 2;
106 	count = smbnhgets(pdata); pdata += 2;
107 	offset = smbnhgetl(pdata);
108 
109 	smblogprint(SMB_COM_WRITE, "smbcomwrite: fid 0x%.4ux count 0x%.4ux offset 0x%.8lux\n",
110 		fid, count, offset);
111 
112 	if (!smbbuffergetb(b, &fmt)
113 		|| fmt != 1
114 		|| !smbbuffergets(b, &yacount)
115 		|| yacount != count
116 		|| smbbufferreadspace(b) < count)
117 		return SmbProcessResultFormat;
118 
119 	t = smbidmapfind(s->tidmap, h->tid);
120 	if (t == nil) {
121 		smbseterror(s, ERRSRV, ERRinvtid);
122 		return SmbProcessResultError;
123 	}
124 	f = smbidmapfind(s->fidmap, fid);
125 	if (f == nil) {
126 		smbseterror(s, ERRDOS, ERRbadfid);
127 		return SmbProcessResultError;
128 	}
129 
130 	if (!f->ioallowed) {
131 		smbseterror(s, ERRDOS, ERRbadaccess);
132 		return SmbProcessResultError;
133 	}
134 
135 	if (count == 0) {
136 		SmbProcessResult pr = smbtruncatefile(s, f, offset);
137 		if (pr != SmbProcessResultReply)
138 			return pr;
139 		nb = 0;
140 	}
141 	else {
142 		seek(f->fd, offset, 0);
143 		nb = write(f->fd, smbbufferreadpointer(b), count);
144 		if (nb < 0) {
145 			smbseterror(s, ERRDOS, ERRnoaccess);
146 			return SmbProcessResultError;
147 		}
148 	}
149 	h->wordcount = 1;
150 	if (!smbbufferputheader(s->response, h, &s->peerinfo)
151 		|| !smbbufferputs(s->response, nb)
152 		|| !smbbufferputs(s->response, 0))
153 		return SmbProcessResultMisc;
154 	return SmbProcessResultReply;
155 }
156