xref: /plan9/sys/src/cmd/ip/imap4d/folder.c (revision 43aadf5e5598b9159a98f64e309c3ae860328a56)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <auth.h>
57dd7cddfSDavid du Colombier #include "imap4d.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier static	int	copyData(int ffd, int tfd, MbLock *ml);
87dd7cddfSDavid du Colombier static	MbLock	mLock =
97dd7cddfSDavid du Colombier {
107dd7cddfSDavid du Colombier 	.fd = -1
117dd7cddfSDavid du Colombier };
127dd7cddfSDavid du Colombier 
139a747e4fSDavid du Colombier static char curDir[MboxNameLen];
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier void
resetCurDir(void)167dd7cddfSDavid du Colombier resetCurDir(void)
177dd7cddfSDavid du Colombier {
187dd7cddfSDavid du Colombier 	curDir[0] = '\0';
197dd7cddfSDavid du Colombier }
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier int
myChdir(char * dir)227dd7cddfSDavid du Colombier myChdir(char *dir)
237dd7cddfSDavid du Colombier {
247dd7cddfSDavid du Colombier 	if(strcmp(dir, curDir) == 0)
257dd7cddfSDavid du Colombier 		return 0;
269a747e4fSDavid du Colombier 	if(dir[0] != '/' || strlen(dir) > MboxNameLen)
277dd7cddfSDavid du Colombier 		return -1;
287dd7cddfSDavid du Colombier 	strcpy(curDir, dir);
297dd7cddfSDavid du Colombier 	if(chdir(dir) < 0){
309a747e4fSDavid du Colombier 		werrstr("mychdir failed: %r");
317dd7cddfSDavid du Colombier 		return -1;
327dd7cddfSDavid du Colombier 	}
337dd7cddfSDavid du Colombier 	return 0;
347dd7cddfSDavid du Colombier }
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier int
cdCreate(char * dir,char * file,int mode,ulong perm)377dd7cddfSDavid du Colombier cdCreate(char *dir, char *file, int mode, ulong perm)
387dd7cddfSDavid du Colombier {
397dd7cddfSDavid du Colombier 	if(myChdir(dir) < 0)
407dd7cddfSDavid du Colombier 		return -1;
417dd7cddfSDavid du Colombier 	return create(file, mode, perm);
427dd7cddfSDavid du Colombier }
437dd7cddfSDavid du Colombier 
449a747e4fSDavid du Colombier Dir*
cdDirstat(char * dir,char * file)459a747e4fSDavid du Colombier cdDirstat(char *dir, char *file)
467dd7cddfSDavid du Colombier {
477dd7cddfSDavid du Colombier 	if(myChdir(dir) < 0)
489a747e4fSDavid du Colombier 		return nil;
499a747e4fSDavid du Colombier 	return dirstat(file);
509a747e4fSDavid du Colombier }
519a747e4fSDavid du Colombier 
529a747e4fSDavid du Colombier int
cdExists(char * dir,char * file)539a747e4fSDavid du Colombier cdExists(char *dir, char *file)
549a747e4fSDavid du Colombier {
559a747e4fSDavid du Colombier 	Dir *d;
569a747e4fSDavid du Colombier 
579a747e4fSDavid du Colombier 	d = cdDirstat(dir, file);
589a747e4fSDavid du Colombier 	if(d == nil)
599a747e4fSDavid du Colombier 		return 0;
609a747e4fSDavid du Colombier 	free(d);
619a747e4fSDavid du Colombier 	return 1;
627dd7cddfSDavid du Colombier }
637dd7cddfSDavid du Colombier 
647dd7cddfSDavid du Colombier int
cdDirwstat(char * dir,char * file,Dir * d)657dd7cddfSDavid du Colombier cdDirwstat(char *dir, char *file, Dir *d)
667dd7cddfSDavid du Colombier {
677dd7cddfSDavid du Colombier 	if(myChdir(dir) < 0)
687dd7cddfSDavid du Colombier 		return -1;
697dd7cddfSDavid du Colombier 	return dirwstat(file, d);
707dd7cddfSDavid du Colombier }
717dd7cddfSDavid du Colombier 
727dd7cddfSDavid du Colombier int
cdOpen(char * dir,char * file,int mode)737dd7cddfSDavid du Colombier cdOpen(char *dir, char *file, int mode)
747dd7cddfSDavid du Colombier {
757dd7cddfSDavid du Colombier 	if(myChdir(dir) < 0)
767dd7cddfSDavid du Colombier 		return -1;
777dd7cddfSDavid du Colombier 	return open(file, mode);
787dd7cddfSDavid du Colombier }
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier int
cdRemove(char * dir,char * file)817dd7cddfSDavid du Colombier cdRemove(char *dir, char *file)
827dd7cddfSDavid du Colombier {
837dd7cddfSDavid du Colombier 	if(myChdir(dir) < 0)
847dd7cddfSDavid du Colombier 		return -1;
857dd7cddfSDavid du Colombier 	return remove(file);
867dd7cddfSDavid du Colombier }
877dd7cddfSDavid du Colombier 
887dd7cddfSDavid du Colombier /*
897dd7cddfSDavid du Colombier  * open the one true mail lock file
907dd7cddfSDavid du Colombier  */
917dd7cddfSDavid du Colombier MbLock*
mbLock(void)927dd7cddfSDavid du Colombier mbLock(void)
937dd7cddfSDavid du Colombier {
94*43aadf5eSDavid du Colombier 	int i;
95*43aadf5eSDavid du Colombier 
967dd7cddfSDavid du Colombier 	if(mLock.fd >= 0)
977dd7cddfSDavid du Colombier 		bye("mail lock deadlock");
98*43aadf5eSDavid du Colombier 	for(i = 0; i < 5; i++){
997dd7cddfSDavid du Colombier 		mLock.fd = openLocked(mboxDir, "L.mbox", OREAD);
1007dd7cddfSDavid du Colombier 		if(mLock.fd >= 0)
1017dd7cddfSDavid du Colombier 			return &mLock;
102*43aadf5eSDavid du Colombier 		sleep(1000);
103*43aadf5eSDavid du Colombier 	}
1047dd7cddfSDavid du Colombier 	return nil;
1057dd7cddfSDavid du Colombier }
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier void
mbUnlock(MbLock * ml)1087dd7cddfSDavid du Colombier mbUnlock(MbLock *ml)
1097dd7cddfSDavid du Colombier {
1107dd7cddfSDavid du Colombier 	if(ml != &mLock)
1117dd7cddfSDavid du Colombier 		bye("bad mail unlock");
1127dd7cddfSDavid du Colombier 	if(ml->fd < 0)
1137dd7cddfSDavid du Colombier 		bye("mail unlock when not locked");
1147dd7cddfSDavid du Colombier 	close(ml->fd);
1157dd7cddfSDavid du Colombier 	ml->fd = -1;
1167dd7cddfSDavid du Colombier }
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier void
mbLockRefresh(MbLock * ml)1197dd7cddfSDavid du Colombier mbLockRefresh(MbLock *ml)
1207dd7cddfSDavid du Colombier {
1217dd7cddfSDavid du Colombier 	char buf[1];
1227dd7cddfSDavid du Colombier 
1237dd7cddfSDavid du Colombier 	seek(ml->fd, 0, 0);
1247dd7cddfSDavid du Colombier 	read(ml->fd, buf, 1);
1257dd7cddfSDavid du Colombier }
1267dd7cddfSDavid du Colombier 
1277dd7cddfSDavid du Colombier int
mbLocked(void)1287dd7cddfSDavid du Colombier mbLocked(void)
1297dd7cddfSDavid du Colombier {
1307dd7cddfSDavid du Colombier 	return mLock.fd >= 0;
1317dd7cddfSDavid du Colombier }
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier char*
impName(char * name)1347dd7cddfSDavid du Colombier impName(char *name)
1357dd7cddfSDavid du Colombier {
1367dd7cddfSDavid du Colombier 	char *s;
1377dd7cddfSDavid du Colombier 	int n;
1387dd7cddfSDavid du Colombier 
1397dd7cddfSDavid du Colombier 	if(cistrcmp(name, "inbox") == 0)
140*43aadf5eSDavid du Colombier 		if(access("msgs", AEXIST) == 0)
141*43aadf5eSDavid du Colombier 			name = "msgs";
142*43aadf5eSDavid du Colombier 		else
1437dd7cddfSDavid du Colombier 			name = "mbox";
1447dd7cddfSDavid du Colombier 	n = strlen(name) + STRLEN(".imp") + 1;
14580ee5cbfSDavid du Colombier 	s = binalloc(&parseBin, n, 0);
1467dd7cddfSDavid du Colombier 	if(s == nil)
1477dd7cddfSDavid du Colombier 		return nil;
1487dd7cddfSDavid du Colombier 	snprint(s, n, "%s.imp", name);
1497dd7cddfSDavid du Colombier 	return s;
1507dd7cddfSDavid du Colombier }
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier /*
1537dd7cddfSDavid du Colombier  * massage the mailbox name into something valid
1547dd7cddfSDavid du Colombier  * eliminates all .', and ..',s, redundatant and trailing /'s.
1557dd7cddfSDavid du Colombier  */
1567dd7cddfSDavid du Colombier char *
mboxName(char * s)1577dd7cddfSDavid du Colombier mboxName(char *s)
1587dd7cddfSDavid du Colombier {
1597dd7cddfSDavid du Colombier 	char *ss;
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	ss = mutf7str(s);
1627dd7cddfSDavid du Colombier 	if(ss == nil)
1637dd7cddfSDavid du Colombier 		return nil;
1647dd7cddfSDavid du Colombier 	cleanname(ss);
1657dd7cddfSDavid du Colombier 	return ss;
1667dd7cddfSDavid du Colombier }
1677dd7cddfSDavid du Colombier 
1687dd7cddfSDavid du Colombier char *
strmutf7(char * s)1697dd7cddfSDavid du Colombier strmutf7(char *s)
1707dd7cddfSDavid du Colombier {
1717dd7cddfSDavid du Colombier 	char *m;
1727dd7cddfSDavid du Colombier 	int n;
1737dd7cddfSDavid du Colombier 
1747dd7cddfSDavid du Colombier 	n = strlen(s) * MUtf7Max + 1;
17580ee5cbfSDavid du Colombier 	m = binalloc(&parseBin, n, 0);
1767dd7cddfSDavid du Colombier 	if(m == nil)
1777dd7cddfSDavid du Colombier 		return nil;
1787dd7cddfSDavid du Colombier 	if(encmutf7(m, n, s) < 0)
1797dd7cddfSDavid du Colombier 		return nil;
1807dd7cddfSDavid du Colombier 	return m;
1817dd7cddfSDavid du Colombier }
1827dd7cddfSDavid du Colombier 
1837dd7cddfSDavid du Colombier char *
mutf7str(char * s)1847dd7cddfSDavid du Colombier mutf7str(char *s)
1857dd7cddfSDavid du Colombier {
1867dd7cddfSDavid du Colombier 	char *m;
1877dd7cddfSDavid du Colombier 	int n;
1887dd7cddfSDavid du Colombier 
1897dd7cddfSDavid du Colombier 	/*
1907dd7cddfSDavid du Colombier 	 * n = strlen(s) * UTFmax / (2.67) + 1
1917dd7cddfSDavid du Colombier 	 * UTFMax / 2.67 == 3 / (8/3) == 9 / 8
1927dd7cddfSDavid du Colombier 	 */
1937dd7cddfSDavid du Colombier 	n = strlen(s);
1947dd7cddfSDavid du Colombier 	n = (n * 9 + 7) / 8 + 1;
19580ee5cbfSDavid du Colombier 	m = binalloc(&parseBin, n, 0);
1967dd7cddfSDavid du Colombier 	if(m == nil)
1977dd7cddfSDavid du Colombier 		return nil;
1987dd7cddfSDavid du Colombier 	if(decmutf7(m, n, s) < 0)
1997dd7cddfSDavid du Colombier 		return nil;
2007dd7cddfSDavid du Colombier 	return m;
2017dd7cddfSDavid du Colombier }
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier void
splitr(char * s,int c,char ** left,char ** right)2047dd7cddfSDavid du Colombier splitr(char *s, int c, char **left, char **right)
2057dd7cddfSDavid du Colombier {
2067dd7cddfSDavid du Colombier 	char *d;
2077dd7cddfSDavid du Colombier 	int n;
2087dd7cddfSDavid du Colombier 
2097dd7cddfSDavid du Colombier 	n = strlen(s);
21080ee5cbfSDavid du Colombier 	d = binalloc(&parseBin, n + 1, 0);
2117dd7cddfSDavid du Colombier 	if(d == nil)
2127dd7cddfSDavid du Colombier 		parseErr("out of memory");
2137dd7cddfSDavid du Colombier 	strcpy(d, s);
2147dd7cddfSDavid du Colombier 	s = strrchr(d, c);
2157dd7cddfSDavid du Colombier 	if(s != nil){
2167dd7cddfSDavid du Colombier 		*left = d;
2177dd7cddfSDavid du Colombier 		*s++ = '\0';
2187dd7cddfSDavid du Colombier 		*right = s;
2197dd7cddfSDavid du Colombier 	}else{
2207dd7cddfSDavid du Colombier 		*right = d;
2217dd7cddfSDavid du Colombier 		*left = d + n;
2227dd7cddfSDavid du Colombier 	}
2237dd7cddfSDavid du Colombier }
2247dd7cddfSDavid du Colombier 
2257dd7cddfSDavid du Colombier /*
2267dd7cddfSDavid du Colombier  * create the mailbox and all intermediate components
2277dd7cddfSDavid du Colombier  * a trailing / implies the new mailbox is a directory;
2287dd7cddfSDavid du Colombier  * otherwise, it's a file.
2297dd7cddfSDavid du Colombier  *
2307dd7cddfSDavid du Colombier  * return with the file open for write, or directory open for read.
2317dd7cddfSDavid du Colombier  */
2327dd7cddfSDavid du Colombier int
createBox(char * mbox,int dir)2337dd7cddfSDavid du Colombier createBox(char *mbox, int dir)
2347dd7cddfSDavid du Colombier {
2357dd7cddfSDavid du Colombier 	char *m;
2367dd7cddfSDavid du Colombier 	int fd;
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier 	fd = -1;
2397dd7cddfSDavid du Colombier 	for(m = mbox; *m; m++){
2407dd7cddfSDavid du Colombier 		if(*m == '/'){
2417dd7cddfSDavid du Colombier 			*m = '\0';
2427dd7cddfSDavid du Colombier 			if(access(mbox, AEXIST) < 0){
2437dd7cddfSDavid du Colombier 				if(fd >= 0)
2447dd7cddfSDavid du Colombier 					close(fd);
2459a747e4fSDavid du Colombier 				fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
2467dd7cddfSDavid du Colombier 				if(fd < 0)
2477dd7cddfSDavid du Colombier 					return -1;
2487dd7cddfSDavid du Colombier 			}
2497dd7cddfSDavid du Colombier 			*m = '/';
2507dd7cddfSDavid du Colombier 		}
2517dd7cddfSDavid du Colombier 	}
2527dd7cddfSDavid du Colombier 	if(dir)
2539a747e4fSDavid du Colombier 		fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
2547dd7cddfSDavid du Colombier 	else
2557dd7cddfSDavid du Colombier 		fd = cdCreate(mboxDir, mbox, OWRITE, 0664);
2567dd7cddfSDavid du Colombier 	return fd;
2577dd7cddfSDavid du Colombier }
2587dd7cddfSDavid du Colombier 
2597dd7cddfSDavid du Colombier /*
2607dd7cddfSDavid du Colombier  * move one mail folder to another
2617dd7cddfSDavid du Colombier  * destination mailbox doesn't exist.
2627dd7cddfSDavid du Colombier  * the source folder may be a directory or a mailbox,
2637dd7cddfSDavid du Colombier  * and may be in the same directory as the destination,
2647dd7cddfSDavid du Colombier  * or a completely different directory.
2657dd7cddfSDavid du Colombier  */
2667dd7cddfSDavid du Colombier int
moveBox(char * from,char * to)2677dd7cddfSDavid du Colombier moveBox(char *from, char *to)
2687dd7cddfSDavid du Colombier {
2699a747e4fSDavid du Colombier 	Dir *d;
2707dd7cddfSDavid du Colombier 	char *fd, *fe, *td, *te, *fimp;
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 	splitr(from, '/', &fd, &fe);
2737dd7cddfSDavid du Colombier 	splitr(to, '/', &td, &te);
2747dd7cddfSDavid du Colombier 
2757dd7cddfSDavid du Colombier 	/*
2767dd7cddfSDavid du Colombier 	 * in the same directory: try rename
2777dd7cddfSDavid du Colombier 	 */
2789a747e4fSDavid du Colombier 	d = cdDirstat(mboxDir, from);
2799a747e4fSDavid du Colombier 	if(d == nil)
2807dd7cddfSDavid du Colombier 		return 0;
2817dd7cddfSDavid du Colombier 	if(strcmp(fd, td) == 0){
2829a747e4fSDavid du Colombier 		nulldir(d);
2839a747e4fSDavid du Colombier 		d->name = te;
2849a747e4fSDavid du Colombier 		if(cdDirwstat(mboxDir, from, d) >= 0){
2857dd7cddfSDavid du Colombier 			fimp = impName(from);
2869a747e4fSDavid du Colombier 			d->name = impName(te);
2879a747e4fSDavid du Colombier 			cdDirwstat(mboxDir, fimp, d);
2889a747e4fSDavid du Colombier 			free(d);
2897dd7cddfSDavid du Colombier 			return 1;
2907dd7cddfSDavid du Colombier 		}
2917dd7cddfSDavid du Colombier 	}
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 	/*
2947dd7cddfSDavid du Colombier 	 * directory copy is too hard for now
2957dd7cddfSDavid du Colombier 	 */
2969a747e4fSDavid du Colombier 	if(d->mode & DMDIR)
2977dd7cddfSDavid du Colombier 		return 0;
2989a747e4fSDavid du Colombier 	free(d);
2997dd7cddfSDavid du Colombier 
3007dd7cddfSDavid du Colombier 	return copyBox(from, to, 1);
3017dd7cddfSDavid du Colombier }
3027dd7cddfSDavid du Colombier 
3037dd7cddfSDavid du Colombier /*
3047dd7cddfSDavid du Colombier  * copy the contents of one mailbox to another
3057dd7cddfSDavid du Colombier  * either truncates or removes the source box if it succeeds.
3067dd7cddfSDavid du Colombier  */
3077dd7cddfSDavid du Colombier int
copyBox(char * from,char * to,int doremove)3087dd7cddfSDavid du Colombier copyBox(char *from, char *to, int doremove)
3097dd7cddfSDavid du Colombier {
3107dd7cddfSDavid du Colombier 	MbLock *ml;
3117dd7cddfSDavid du Colombier 	char *fimp, *timp;
3127dd7cddfSDavid du Colombier 	int ffd, tfd, ok;
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier 	if(cistrcmp(from, "inbox") == 0)
315*43aadf5eSDavid du Colombier 		if(access("msgs", AEXIST) == 0)
316*43aadf5eSDavid du Colombier 			from = "msgs";
317*43aadf5eSDavid du Colombier 		else
3187dd7cddfSDavid du Colombier 			from = "mbox";
3197dd7cddfSDavid du Colombier 
3207dd7cddfSDavid du Colombier 	ml = mbLock();
3217dd7cddfSDavid du Colombier 	if(ml == nil)
3227dd7cddfSDavid du Colombier 		return 0;
3237dd7cddfSDavid du Colombier 	ffd = openLocked(mboxDir, from, OREAD);
3247dd7cddfSDavid du Colombier 	if(ffd < 0){
3257dd7cddfSDavid du Colombier 		mbUnlock(ml);
3267dd7cddfSDavid du Colombier 		return 0;
3277dd7cddfSDavid du Colombier 	}
3287dd7cddfSDavid du Colombier 	tfd = createBox(to, 0);
3297dd7cddfSDavid du Colombier 	if(tfd < 0){
3307dd7cddfSDavid du Colombier 		mbUnlock(ml);
3317dd7cddfSDavid du Colombier 		close(ffd);
3327dd7cddfSDavid du Colombier 		return 0;
3337dd7cddfSDavid du Colombier 	}
3347dd7cddfSDavid du Colombier 
3357dd7cddfSDavid du Colombier 	ok = copyData(ffd, tfd, ml);
3367dd7cddfSDavid du Colombier 	close(ffd);
3377dd7cddfSDavid du Colombier 	close(tfd);
3387dd7cddfSDavid du Colombier 	if(!ok){
3397dd7cddfSDavid du Colombier 		mbUnlock(ml);
3407dd7cddfSDavid du Colombier 		return 0;
3417dd7cddfSDavid du Colombier 	}
3427dd7cddfSDavid du Colombier 
3437dd7cddfSDavid du Colombier 	fimp = impName(from);
3447dd7cddfSDavid du Colombier 	timp = impName(to);
3457dd7cddfSDavid du Colombier 	if(fimp != nil && timp != nil){
3467dd7cddfSDavid du Colombier 		ffd = cdOpen(mboxDir, fimp, OREAD);
3477dd7cddfSDavid du Colombier 		if(ffd >= 0){
3487dd7cddfSDavid du Colombier 			tfd = cdCreate(mboxDir, timp, OWRITE, 0664);
3497dd7cddfSDavid du Colombier 			if(tfd >= 0){
3507dd7cddfSDavid du Colombier 				copyData(ffd, tfd, ml);
3517dd7cddfSDavid du Colombier 				close(tfd);
3527dd7cddfSDavid du Colombier 			}
3537dd7cddfSDavid du Colombier 			close(ffd);
3547dd7cddfSDavid du Colombier 		}
3557dd7cddfSDavid du Colombier 	}
3567dd7cddfSDavid du Colombier 	cdRemove(mboxDir, fimp);
3577dd7cddfSDavid du Colombier 	if(doremove)
3587dd7cddfSDavid du Colombier 		cdRemove(mboxDir, from);
3597dd7cddfSDavid du Colombier 	else
3607dd7cddfSDavid du Colombier 		close(cdOpen(mboxDir, from, OWRITE|OTRUNC));
3617dd7cddfSDavid du Colombier 	mbUnlock(ml);
3627dd7cddfSDavid du Colombier 	return 1;
3637dd7cddfSDavid du Colombier }
3647dd7cddfSDavid du Colombier 
3657dd7cddfSDavid du Colombier /*
3667dd7cddfSDavid du Colombier  * copies while holding the mail lock,
3677dd7cddfSDavid du Colombier  * then tries to copy permissions and group ownership
3687dd7cddfSDavid du Colombier  */
3697dd7cddfSDavid du Colombier static int
copyData(int ffd,int tfd,MbLock * ml)3707dd7cddfSDavid du Colombier copyData(int ffd, int tfd, MbLock *ml)
3717dd7cddfSDavid du Colombier {
3729a747e4fSDavid du Colombier 	Dir *fd, td;
3737dd7cddfSDavid du Colombier 	char buf[BufSize];
3747dd7cddfSDavid du Colombier 	int n;
3757dd7cddfSDavid du Colombier 
3767dd7cddfSDavid du Colombier 	for(;;){
3777dd7cddfSDavid du Colombier 		n = read(ffd, buf, BufSize);
3787dd7cddfSDavid du Colombier 		if(n <= 0){
3797dd7cddfSDavid du Colombier 			if(n < 0)
3807dd7cddfSDavid du Colombier 				return 0;
3817dd7cddfSDavid du Colombier 			break;
3827dd7cddfSDavid du Colombier 		}
3837dd7cddfSDavid du Colombier 		if(write(tfd, buf, n) != n)
3847dd7cddfSDavid du Colombier 			return 0;
3857dd7cddfSDavid du Colombier 		mbLockRefresh(ml);
3867dd7cddfSDavid du Colombier 	}
3879a747e4fSDavid du Colombier 	fd = dirfstat(ffd);
3889a747e4fSDavid du Colombier 	if(fd != nil){
3899a747e4fSDavid du Colombier 		nulldir(&td);
3909a747e4fSDavid du Colombier 		td.mode = fd->mode;
3917dd7cddfSDavid du Colombier 		if(dirfwstat(tfd, &td) >= 0){
3929a747e4fSDavid du Colombier 			nulldir(&td);
3939a747e4fSDavid du Colombier 			td.gid = fd->gid;
3947dd7cddfSDavid du Colombier 			dirfwstat(tfd, &td);
3957dd7cddfSDavid du Colombier 		}
3967dd7cddfSDavid du Colombier 	}
3977dd7cddfSDavid du Colombier 	return 1;
3987dd7cddfSDavid du Colombier }
399