16007Sthurlow /* 26007Sthurlow * Copyright (c) 2000, Boris Popov 36007Sthurlow * All rights reserved. 46007Sthurlow * 56007Sthurlow * Redistribution and use in source and binary forms, with or without 66007Sthurlow * modification, are permitted provided that the following conditions 76007Sthurlow * are met: 86007Sthurlow * 1. Redistributions of source code must retain the above copyright 96007Sthurlow * notice, this list of conditions and the following disclaimer. 106007Sthurlow * 2. Redistributions in binary form must reproduce the above copyright 116007Sthurlow * notice, this list of conditions and the following disclaimer in the 126007Sthurlow * documentation and/or other materials provided with the distribution. 136007Sthurlow * 3. All advertising materials mentioning features or use of this software 146007Sthurlow * must display the following acknowledgement: 156007Sthurlow * This product includes software developed by Boris Popov. 166007Sthurlow * 4. Neither the name of the author nor the names of any co-contributors 176007Sthurlow * may be used to endorse or promote products derived from this software 186007Sthurlow * without specific prior written permission. 196007Sthurlow * 206007Sthurlow * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 216007Sthurlow * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 226007Sthurlow * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 236007Sthurlow * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 246007Sthurlow * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 256007Sthurlow * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 266007Sthurlow * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 276007Sthurlow * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 286007Sthurlow * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 296007Sthurlow * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 306007Sthurlow * SUCH DAMAGE. 316007Sthurlow * 326007Sthurlow * $Id: file.c,v 1.4 2004/12/13 00:25:21 lindak Exp $ 336007Sthurlow */ 346007Sthurlow 358271SGordon.Ross@Sun.COM /* 36*10023SGordon.Ross@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 378271SGordon.Ross@Sun.COM * Use is subject to license terms. 388271SGordon.Ross@Sun.COM */ 396007Sthurlow 406007Sthurlow #include <sys/param.h> 416007Sthurlow #include <sys/ioctl.h> 426007Sthurlow #include <sys/time.h> 436007Sthurlow #include <sys/mount.h> 446007Sthurlow #include <fcntl.h> 456007Sthurlow #include <ctype.h> 466007Sthurlow #include <errno.h> 476007Sthurlow #include <stdio.h> 486007Sthurlow #include <string.h> 496007Sthurlow #include <strings.h> 506007Sthurlow #include <stdlib.h> 516007Sthurlow #include <pwd.h> 526007Sthurlow #include <grp.h> 536007Sthurlow #include <unistd.h> 548271SGordon.Ross@Sun.COM #include <libintl.h> 556007Sthurlow 566007Sthurlow #include <sys/types.h> 578271SGordon.Ross@Sun.COM #include <sys/file.h> 586007Sthurlow 59*10023SGordon.Ross@Sun.COM #include <netsmb/smb.h> 606007Sthurlow #include <netsmb/smb_lib.h> 616007Sthurlow 628271SGordon.Ross@Sun.COM #include "private.h" 638271SGordon.Ross@Sun.COM 646007Sthurlow int 65*10023SGordon.Ross@Sun.COM smb_fh_close(struct smb_ctx *ctx, int fh) 668271SGordon.Ross@Sun.COM { 678271SGordon.Ross@Sun.COM struct smb_rq *rqp; 688271SGordon.Ross@Sun.COM struct mbdata *mbp; 69*10023SGordon.Ross@Sun.COM int error; 708271SGordon.Ross@Sun.COM 71*10023SGordon.Ross@Sun.COM error = smb_rq_init(ctx, SMB_COM_CLOSE, &rqp); 72*10023SGordon.Ross@Sun.COM if (error != 0) 73*10023SGordon.Ross@Sun.COM return (error); 748271SGordon.Ross@Sun.COM mbp = smb_rq_getrequest(rqp); 75*10023SGordon.Ross@Sun.COM smb_rq_wstart(rqp); 76*10023SGordon.Ross@Sun.COM mb_put_uint16le(mbp, (uint16_t)fh); 778271SGordon.Ross@Sun.COM mb_put_uint32le(mbp, 0); /* time stamp */ 788271SGordon.Ross@Sun.COM smb_rq_wend(rqp); 79*10023SGordon.Ross@Sun.COM mb_put_uint16le(mbp, 0); /* byte count */ 80*10023SGordon.Ross@Sun.COM 81*10023SGordon.Ross@Sun.COM error = smb_rq_simple(rqp); 828271SGordon.Ross@Sun.COM smb_rq_done(rqp); 838271SGordon.Ross@Sun.COM 84*10023SGordon.Ross@Sun.COM return (error); 858271SGordon.Ross@Sun.COM } 868271SGordon.Ross@Sun.COM 878271SGordon.Ross@Sun.COM int 888271SGordon.Ross@Sun.COM smb_fh_ntcreate( 898271SGordon.Ross@Sun.COM struct smb_ctx *ctx, char *path, 908271SGordon.Ross@Sun.COM int flags, int req_acc, int efattr, 918271SGordon.Ross@Sun.COM int share_acc, int open_disp, 928271SGordon.Ross@Sun.COM int create_opts, int impersonation, 93*10023SGordon.Ross@Sun.COM int *fhp, uint32_t *action_taken) 948271SGordon.Ross@Sun.COM { 958271SGordon.Ross@Sun.COM struct smb_rq *rqp; 968271SGordon.Ross@Sun.COM struct mbdata *mbp; 97*10023SGordon.Ross@Sun.COM char *pathsizep; 98*10023SGordon.Ross@Sun.COM int pathstart, pathsize; 99*10023SGordon.Ross@Sun.COM int error, flags2, uc; 100*10023SGordon.Ross@Sun.COM uint16_t fh; 1018271SGordon.Ross@Sun.COM uint8_t wc; 1028271SGordon.Ross@Sun.COM 1038271SGordon.Ross@Sun.COM flags2 = smb_ctx_flags2(ctx); 1048271SGordon.Ross@Sun.COM if (flags2 == -1) 1058271SGordon.Ross@Sun.COM return (EIO); 106*10023SGordon.Ross@Sun.COM uc = flags2 & SMB_FLAGS2_UNICODE; 1078271SGordon.Ross@Sun.COM 108*10023SGordon.Ross@Sun.COM error = smb_rq_init(ctx, SMB_COM_NT_CREATE_ANDX, &rqp); 1098271SGordon.Ross@Sun.COM if (error != 0) 1108271SGordon.Ross@Sun.COM return (error); 1118271SGordon.Ross@Sun.COM 1128271SGordon.Ross@Sun.COM mbp = smb_rq_getrequest(rqp); 113*10023SGordon.Ross@Sun.COM smb_rq_wstart(rqp); 114*10023SGordon.Ross@Sun.COM mb_put_uint16le(mbp, 0xff); /* secondary command */ 1158271SGordon.Ross@Sun.COM mb_put_uint16le(mbp, 0); /* offset to next command (none) */ 116*10023SGordon.Ross@Sun.COM mb_put_uint8(mbp, 0); /* MBZ (pad?) */ 117*10023SGordon.Ross@Sun.COM mb_fit(mbp, 2, &pathsizep); /* path size - fill in below */ 118*10023SGordon.Ross@Sun.COM mb_put_uint32le(mbp, flags); /* create flags (oplock) */ 1198271SGordon.Ross@Sun.COM mb_put_uint32le(mbp, 0); /* FID - basis for path if not root */ 1208271SGordon.Ross@Sun.COM mb_put_uint32le(mbp, req_acc); 1218271SGordon.Ross@Sun.COM mb_put_uint64le(mbp, 0); /* initial alloc. size */ 1228271SGordon.Ross@Sun.COM mb_put_uint32le(mbp, efattr); /* ext. file attributes */ 1238271SGordon.Ross@Sun.COM mb_put_uint32le(mbp, share_acc); /* share access mode */ 1248271SGordon.Ross@Sun.COM mb_put_uint32le(mbp, open_disp); /* open disposition */ 1258271SGordon.Ross@Sun.COM mb_put_uint32le(mbp, create_opts); /* create_options */ 126*10023SGordon.Ross@Sun.COM mb_put_uint32le(mbp, impersonation); 1278271SGordon.Ross@Sun.COM mb_put_uint8(mbp, 0); /* security flags (?) */ 1288271SGordon.Ross@Sun.COM smb_rq_wend(rqp); 129*10023SGordon.Ross@Sun.COM smb_rq_bstart(rqp); 130*10023SGordon.Ross@Sun.COM if (uc) { 131*10023SGordon.Ross@Sun.COM /* 132*10023SGordon.Ross@Sun.COM * We're about to put a unicode string. We know 133*10023SGordon.Ross@Sun.COM * we're misaligned at this point, and need to 134*10023SGordon.Ross@Sun.COM * save the mb_count at the start of the string, 135*10023SGordon.Ross@Sun.COM * not at the alignment padding placed before it. 136*10023SGordon.Ross@Sun.COM * So add the algnment padding by hand here. 137*10023SGordon.Ross@Sun.COM */ 138*10023SGordon.Ross@Sun.COM mb_put_uint8(mbp, 0); 139*10023SGordon.Ross@Sun.COM } 140*10023SGordon.Ross@Sun.COM pathstart = mbp->mb_count; 141*10023SGordon.Ross@Sun.COM mb_put_dstring(mbp, path, uc); 142*10023SGordon.Ross@Sun.COM smb_rq_bend(rqp); 1438271SGordon.Ross@Sun.COM 144*10023SGordon.Ross@Sun.COM /* Now go back and fill in pathsizep */ 145*10023SGordon.Ross@Sun.COM pathsize = mbp->mb_count - pathstart; 146*10023SGordon.Ross@Sun.COM pathsizep[0] = pathsize & 0xFF; 147*10023SGordon.Ross@Sun.COM pathsizep[1] = (pathsize >> 8); 1488271SGordon.Ross@Sun.COM 1498271SGordon.Ross@Sun.COM error = smb_rq_simple(rqp); 1508271SGordon.Ross@Sun.COM if (error) 1518271SGordon.Ross@Sun.COM goto out; 1528271SGordon.Ross@Sun.COM 1538271SGordon.Ross@Sun.COM mbp = smb_rq_getreply(rqp); 1548271SGordon.Ross@Sun.COM /* 1558271SGordon.Ross@Sun.COM * spec says 26 for word count, but 34 words are defined 1568271SGordon.Ross@Sun.COM * and observed from win2000 1578271SGordon.Ross@Sun.COM */ 158*10023SGordon.Ross@Sun.COM error = mb_get_uint8(mbp, &wc); 159*10023SGordon.Ross@Sun.COM if (error || wc < 26) { 1608271SGordon.Ross@Sun.COM smb_error(dgettext(TEXT_DOMAIN, 1618271SGordon.Ross@Sun.COM "%s: open failed, bad word count"), 0, path); 1628271SGordon.Ross@Sun.COM error = EBADRPC; 1638271SGordon.Ross@Sun.COM goto out; 1648271SGordon.Ross@Sun.COM } 1658271SGordon.Ross@Sun.COM mb_get_uint8(mbp, NULL); /* secondary cmd */ 1668271SGordon.Ross@Sun.COM mb_get_uint8(mbp, NULL); /* mbz */ 1678271SGordon.Ross@Sun.COM mb_get_uint16le(mbp, NULL); /* andxoffset */ 1688271SGordon.Ross@Sun.COM mb_get_uint8(mbp, NULL); /* oplock lvl granted */ 169*10023SGordon.Ross@Sun.COM mb_get_uint16le(mbp, &fh); /* FID */ 1708271SGordon.Ross@Sun.COM mb_get_uint32le(mbp, action_taken); 1718271SGordon.Ross@Sun.COM #if 0 /* skip decoding the rest */ 1728271SGordon.Ross@Sun.COM mb_get_uint64le(mbp, NULL); /* creation time */ 1738271SGordon.Ross@Sun.COM mb_get_uint64le(mbp, NULL); /* access time */ 1748271SGordon.Ross@Sun.COM mb_get_uint64le(mbp, NULL); /* write time */ 1758271SGordon.Ross@Sun.COM mb_get_uint64le(mbp, NULL); /* change time */ 1768271SGordon.Ross@Sun.COM mb_get_uint32le(mbp, NULL); /* attributes */ 1778271SGordon.Ross@Sun.COM mb_get_uint64le(mbp, NULL); /* allocation size */ 1788271SGordon.Ross@Sun.COM mb_get_uint64le(mbp, NULL); /* EOF */ 1798271SGordon.Ross@Sun.COM mb_get_uint16le(mbp, NULL); /* file type */ 1808271SGordon.Ross@Sun.COM mb_get_uint16le(mbp, NULL); /* device state */ 1818271SGordon.Ross@Sun.COM mb_get_uint8(mbp, NULL); /* directory (boolean) */ 182*10023SGordon.Ross@Sun.COM #endif 183*10023SGordon.Ross@Sun.COM 184*10023SGordon.Ross@Sun.COM /* success! */ 185*10023SGordon.Ross@Sun.COM *fhp = fh; 186*10023SGordon.Ross@Sun.COM error = 0; 1878271SGordon.Ross@Sun.COM 1888271SGordon.Ross@Sun.COM out: 1898271SGordon.Ross@Sun.COM smb_rq_done(rqp); 1908271SGordon.Ross@Sun.COM 191*10023SGordon.Ross@Sun.COM return (error); 1928271SGordon.Ross@Sun.COM } 1938271SGordon.Ross@Sun.COM 1948271SGordon.Ross@Sun.COM /* 1958271SGordon.Ross@Sun.COM * Conveinence wrapper for smb_fh_ntcreate 1968271SGordon.Ross@Sun.COM * Converts Unix-style open call to NTCreate. 1978271SGordon.Ross@Sun.COM */ 1988271SGordon.Ross@Sun.COM int 199*10023SGordon.Ross@Sun.COM smb_fh_open(struct smb_ctx *ctx, const char *path, int oflag, int *fhp) 2008271SGordon.Ross@Sun.COM { 2018271SGordon.Ross@Sun.COM int error, mode, open_disp, req_acc, share_acc; 2028271SGordon.Ross@Sun.COM char *p, *ntpath = NULL; 2038271SGordon.Ross@Sun.COM 2048271SGordon.Ross@Sun.COM /* 2058271SGordon.Ross@Sun.COM * Map O_RDONLY, O_WRONLY, O_RDWR 2068271SGordon.Ross@Sun.COM * to FREAD, FWRITE 2078271SGordon.Ross@Sun.COM */ 2088271SGordon.Ross@Sun.COM mode = (oflag & 3) + 1; 2098271SGordon.Ross@Sun.COM 2108271SGordon.Ross@Sun.COM /* 2118271SGordon.Ross@Sun.COM * Compute requested access, share access. 2128271SGordon.Ross@Sun.COM */ 2138271SGordon.Ross@Sun.COM req_acc = ( 2148271SGordon.Ross@Sun.COM STD_RIGHT_READ_CONTROL_ACCESS | 2158271SGordon.Ross@Sun.COM STD_RIGHT_SYNCHRONIZE_ACCESS); 2168271SGordon.Ross@Sun.COM share_acc = NTCREATEX_SHARE_ACCESS_NONE; 2178271SGordon.Ross@Sun.COM if (mode & FREAD) { 2188271SGordon.Ross@Sun.COM req_acc |= ( 2198271SGordon.Ross@Sun.COM SA_RIGHT_FILE_READ_DATA | 2208271SGordon.Ross@Sun.COM SA_RIGHT_FILE_READ_EA | 2218271SGordon.Ross@Sun.COM SA_RIGHT_FILE_READ_ATTRIBUTES); 2228271SGordon.Ross@Sun.COM share_acc |= NTCREATEX_SHARE_ACCESS_READ; 2238271SGordon.Ross@Sun.COM } 2248271SGordon.Ross@Sun.COM if (mode & FWRITE) { 2258271SGordon.Ross@Sun.COM req_acc |= ( 2268271SGordon.Ross@Sun.COM SA_RIGHT_FILE_WRITE_DATA | 2278271SGordon.Ross@Sun.COM SA_RIGHT_FILE_APPEND_DATA | 2288271SGordon.Ross@Sun.COM SA_RIGHT_FILE_WRITE_EA | 2298271SGordon.Ross@Sun.COM SA_RIGHT_FILE_WRITE_ATTRIBUTES); 2308271SGordon.Ross@Sun.COM share_acc |= NTCREATEX_SHARE_ACCESS_WRITE; 2318271SGordon.Ross@Sun.COM } 2328271SGordon.Ross@Sun.COM 2338271SGordon.Ross@Sun.COM /* 2348271SGordon.Ross@Sun.COM * Compute open disposition 2358271SGordon.Ross@Sun.COM */ 2368271SGordon.Ross@Sun.COM if (oflag & FCREAT) { 2378271SGordon.Ross@Sun.COM /* Creat if necessary. */ 2388271SGordon.Ross@Sun.COM if (oflag & FEXCL) { 2398271SGordon.Ross@Sun.COM /* exclusive */ 2408271SGordon.Ross@Sun.COM open_disp = NTCREATEX_DISP_CREATE; 2418271SGordon.Ross@Sun.COM } else if (oflag & FTRUNC) 2428271SGordon.Ross@Sun.COM open_disp = NTCREATEX_DISP_OVERWRITE_IF; 2438271SGordon.Ross@Sun.COM else 2448271SGordon.Ross@Sun.COM open_disp = NTCREATEX_DISP_OPEN_IF; 2458271SGordon.Ross@Sun.COM } else { 2468271SGordon.Ross@Sun.COM /* Not creating. */ 2478271SGordon.Ross@Sun.COM if (oflag & FTRUNC) 2488271SGordon.Ross@Sun.COM open_disp = NTCREATEX_DISP_OVERWRITE; 2498271SGordon.Ross@Sun.COM else 2508271SGordon.Ross@Sun.COM open_disp = NTCREATEX_DISP_OPEN; 2518271SGordon.Ross@Sun.COM } 2528271SGordon.Ross@Sun.COM 2538271SGordon.Ross@Sun.COM /* 2548271SGordon.Ross@Sun.COM * Convert Unix path to NT (backslashes) 2558271SGordon.Ross@Sun.COM */ 2568271SGordon.Ross@Sun.COM ntpath = strdup(path); 2578271SGordon.Ross@Sun.COM if (ntpath == NULL) 2588271SGordon.Ross@Sun.COM return (ENOMEM); 2598271SGordon.Ross@Sun.COM for (p = ntpath; *p; p++) 2608271SGordon.Ross@Sun.COM if (*p == '/') 2618271SGordon.Ross@Sun.COM *p = '\\'; 2628271SGordon.Ross@Sun.COM 2638271SGordon.Ross@Sun.COM error = smb_fh_ntcreate(ctx, ntpath, 0, /* flags */ 2648271SGordon.Ross@Sun.COM req_acc, SMB_EFA_NORMAL, share_acc, open_disp, 2658271SGordon.Ross@Sun.COM NTCREATEX_OPTIONS_NON_DIRECTORY_FILE, 2668271SGordon.Ross@Sun.COM NTCREATEX_IMPERSONATION_IMPERSONATION, 2678271SGordon.Ross@Sun.COM fhp, NULL); 2688271SGordon.Ross@Sun.COM free(ntpath); 2698271SGordon.Ross@Sun.COM 2708271SGordon.Ross@Sun.COM return (error); 2718271SGordon.Ross@Sun.COM } 2728271SGordon.Ross@Sun.COM 2738271SGordon.Ross@Sun.COM int 274*10023SGordon.Ross@Sun.COM smb_fh_read(struct smb_ctx *ctx, int fh, off_t offset, size_t count, 2758271SGordon.Ross@Sun.COM char *dst) 2766007Sthurlow { 2776007Sthurlow struct smbioc_rw rwrq; 2786007Sthurlow 2796007Sthurlow bzero(&rwrq, sizeof (rwrq)); 2806007Sthurlow rwrq.ioc_fh = fh; 2816007Sthurlow rwrq.ioc_base = dst; 2826007Sthurlow rwrq.ioc_cnt = count; 2836007Sthurlow rwrq.ioc_offset = offset; 284*10023SGordon.Ross@Sun.COM if (ioctl(ctx->ct_dev_fd, SMBIOC_READ, &rwrq) == -1) { 2856007Sthurlow return (-1); 2866007Sthurlow } 2876007Sthurlow return (rwrq.ioc_cnt); 2886007Sthurlow } 2896007Sthurlow 2906007Sthurlow int 291*10023SGordon.Ross@Sun.COM smb_fh_write(struct smb_ctx *ctx, int fh, off_t offset, size_t count, 2926007Sthurlow const char *src) 2936007Sthurlow { 2946007Sthurlow struct smbioc_rw rwrq; 2956007Sthurlow 2966007Sthurlow bzero(&rwrq, sizeof (rwrq)); 2976007Sthurlow rwrq.ioc_fh = fh; 2986007Sthurlow rwrq.ioc_base = (char *)src; 2996007Sthurlow rwrq.ioc_cnt = count; 3006007Sthurlow rwrq.ioc_offset = offset; 301*10023SGordon.Ross@Sun.COM if (ioctl(ctx->ct_dev_fd, SMBIOC_WRITE, &rwrq) == -1) { 3026007Sthurlow return (-1); 3036007Sthurlow } 3046007Sthurlow return (rwrq.ioc_cnt); 3056007Sthurlow } 3068271SGordon.Ross@Sun.COM 3078271SGordon.Ross@Sun.COM /* 3088271SGordon.Ross@Sun.COM * Do a TRANSACT_NAMED_PIPE, which is basically just a 3098271SGordon.Ross@Sun.COM * pipe write and pipe read, all in one round trip. 3108271SGordon.Ross@Sun.COM * 3118271SGordon.Ross@Sun.COM * tdlen, tdata describe the data to send. 3128271SGordon.Ross@Sun.COM * rdlen, rdata on input describe the receive buffer, 3138271SGordon.Ross@Sun.COM * and on output *rdlen is the received length. 3148271SGordon.Ross@Sun.COM */ 3158271SGordon.Ross@Sun.COM int 316*10023SGordon.Ross@Sun.COM smb_fh_xactnp(struct smb_ctx *ctx, int fh, 3178271SGordon.Ross@Sun.COM int tdlen, const char *tdata, /* transmit */ 3188271SGordon.Ross@Sun.COM int *rdlen, char *rdata, /* receive */ 3198271SGordon.Ross@Sun.COM int *more) 3208271SGordon.Ross@Sun.COM { 3218271SGordon.Ross@Sun.COM int err, rparamcnt; 3228271SGordon.Ross@Sun.COM uint16_t setup[2]; 3238271SGordon.Ross@Sun.COM 3248271SGordon.Ross@Sun.COM setup[0] = TRANS_TRANSACT_NAMED_PIPE; 3258271SGordon.Ross@Sun.COM setup[1] = fh; 3268271SGordon.Ross@Sun.COM rparamcnt = 0; 3278271SGordon.Ross@Sun.COM 3288271SGordon.Ross@Sun.COM err = smb_t2_request(ctx, 2, setup, "\\PIPE\\", 3298271SGordon.Ross@Sun.COM 0, NULL, /* TX paramcnt, params */ 3308271SGordon.Ross@Sun.COM tdlen, (void *)tdata, 3318271SGordon.Ross@Sun.COM &rparamcnt, NULL, /* no RX params */ 3328271SGordon.Ross@Sun.COM rdlen, rdata, more); 3338271SGordon.Ross@Sun.COM 3348271SGordon.Ross@Sun.COM if (err) 3358271SGordon.Ross@Sun.COM *rdlen = 0; 3368271SGordon.Ross@Sun.COM 3378271SGordon.Ross@Sun.COM return (err); 3388271SGordon.Ross@Sun.COM } 339