/* Netdrive Samba client plugin plugin API Copyright (C) netlabs.org 2003-2012 Copyright (C) bww bitwise works GmbH 2012-2016 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #define NDPL_LARGEFILES #define INCL_LONGLONG #include #include "dircache.h" #include "smbwrp.h" #include "util.h" // ------------------------------------------------------------- /* time conversion functions: SMB protocol sends timestamps in GMT time, * os2 api uses localtime, * emx/klibc uses timezone and daylight saving to convert GMT timestamps, * so only the timezone must be counted in conversion. */ void fsphUnixTimeToDosDate(Resource *pRes, time_t time, FDATE* fdate, FTIME *ftime) { struct tm* gmt = localtime( &time); #if 0 // as localtime() already does dst we don't need to add something if (gmt->tm_isdst>0) { debuglocal(pRes, 9, "daylight saving in effect %d, timezone %d\n",gmt->tm_isdst, timezone); time -= 3600; gmt = localtime( &time); } #endif fdate->day = gmt->tm_mday; fdate->month = gmt->tm_mon+1; fdate->year = gmt->tm_year + 1900 - 1980; ftime->twosecs = gmt->tm_sec/2; ftime->minutes = gmt->tm_min; ftime->hours = gmt->tm_hour; return; } void fsphDosDateToUnixTime(Resource *pRes, FDATE fdate, FTIME ftime, ULONG* time) { struct tm gmtime = { 0 }; debuglocal(pRes, 9, "fsphDosDateToUnixTime time %02d:%02d:%02d\n", ftime.hours, ftime.minutes, ftime.twosecs*2); gmtime.tm_mday = fdate.day; gmtime.tm_mon = fdate.month-1; gmtime.tm_year = fdate.year + 1980 - 1900; gmtime.tm_sec = ftime.twosecs*2; gmtime.tm_min = ftime.minutes; gmtime.tm_hour = ftime.hours; gmtime.tm_isdst = -1; // force libc to check dst saving *time = mktime( &gmtime); debuglocal(pRes, 9, "fsphDosDateToUnixTime time1 %d %s", *time, ctime( (time_t*)time)); #if 0 // as mktime() already does dst we don't need to add something struct tm* gmt; gmt = localtime( (time_t*) time); if (gmt->tm_isdst>0) { debuglocal(pRes, 9, "fsphDosDateToUnixTime daylight saving in effect %d, timezone %d\n",gmt->tm_isdst, timezone); *time += 3600; } debuglocal(pRes, 9, "fsphDosDateToUnixTime time2 %d %s", *time, ctime( (time_t*)time)); #endif return; } // ------------------------------------------------------------- /* uppercased type of resource */ const char *NdpTypes[] = { "SMBFS", NULL } ; /* Properties of supported resource types */ /* Properties of resource */ static const NDPROPERTYINFO smbProperties[] = { {ND_PROP_STRING, 0, "WORKGROUP", ""}, {ND_PROP_STRING, 0, "SERVER", ""}, {ND_PROP_STRING, 0, "SHARE", ""}, {ND_PROP_STRING, 0, "USER", "guest"}, {ND_PROP_STRING, 0, "PASSWORD", ""}, {ND_PROP_STRING, 0, "SPASSWORD", ""}, {ND_PROP_STRING, 0, "MASTER", "WORKGROUP"}, {ND_PROP_ULONG, 0, "MASTERTYPE", "1"}, {ND_PROP_ULONG, 0, "CTO", "10"}, {ND_PROP_ULONG, 0, "CLD", "32"}, {ND_PROP_ULONG, 0, "EASUPPORT", "1"}, {ND_PROP_ULONG, 0, "LOGLEVEL", "0"}, {ND_PROP_STRING, 0, NULL, NULL} }; /* Exported array of properties */ const NDPROPERTYINFO *NdpPropertiesInfo[] = { smbProperties }; PLUGINHELPERTABLE2L *ph; static int ifL; /* A mutex to serialize plugin calls because libsmb may not be thread safe. */ static NDMUTEX mutex; static int lockInit (void) { return ph->fsphCreateMutex (&mutex); } static void lockClose (void) { ph->fsphCloseMutex (mutex); } static int lockRequest (void) { return ph->fsphRequestMutex (mutex, SEM_INDEFINITE_WAIT); } static void lockRelease (void) { ph->fsphReleaseMutex (mutex); } #if LIBSMB_THREAD_SAFE==0 #define ENTER() do { \ int rcLock = lockRequest(); \ if (rcLock != NO_ERROR) \ return rcLock; \ smbwrp_Logging(pRes); \ } while (0) #define LEAVE() do { \ lockRelease(); \ } while (0) #else #define ENTER() do { /* nothing */ } while (0) #define LEAVE() do { /* nothing */ } while (0) #endif int helperEASet (Resource *pRes, cli_state *cli, FEALIST *pFEAList, char *path) { int rc = 0; if (!path || !pFEAList || pFEAList->cbList <= sizeof(long)) return ERROR_EAS_NOT_SUPPORTED; ENTER(); do { // got FEA there FEA * pfea; unsigned long done = sizeof(long); pfea = pFEAList->list; while (done < pFEAList->cbList) { rc = smbwrp_setea(pRes, cli, path, (char*)(pfea + 1), pfea->cbValue ? (char *)(pfea + 1) + pfea->cbName + 1: NULL, pfea->cbValue); if (rc) break; pfea = (FEA *)((char *)(pfea + 1) + pfea->cbName + 1 + pfea->cbValue); done += sizeof(FEA) + pfea->cbName + 1 + pfea->cbValue; } } while (0); LEAVE(); return rc; } /* from ndpl2.c sample: * Helper to concatenate parent path, stored as resource data in * NdpQueryPathInfo and a file object name returned by NdpQueryPathInfo. */ static void buildfullpath2 (char *szFullPath, NDDATABUF *rsrcData, NDFILEINFO *pfi) { *szFullPath = '\0'; if (rsrcData->ulSize > 0) { memcpy (szFullPath, rsrcData->pData, rsrcData->ulSize); } strcat( szFullPath, pfi->pszName); } int APIENTRY NdpPluginLoad (PLUGINHELPERTABLE2L *pPHT) { ph = pPHT; ifL = 0; if (ph->cb >= sizeof (PLUGINHELPERTABLE2L)) ifL = 1; lockInit(); return NO_ERROR; } int APIENTRY NdpPluginFree (void) { debugDelete(); lockClose(); return NO_ERROR; } // // maps smbwrp_fileinfo data fields into a native OS/2 file informations // and add the result to Netdrive internal list // int addFindInfoL(Resource * pRes, void * plist, smbwrp_fileinfo * finfo, ULONG ulAttribute, char * mask) { debuglocal(pRes, 9, "addFindInfoL finfo %p, fname %s\n", finfo, finfo->fname); FILESTATUS3L stat = {0}; char * name = ph->fsphStrRChr(finfo->fname, '\\'); if (name) name++; else name = finfo->fname; if (!*name && pRes != NULL) name = pRes->srv.share_name; if (mask && (!ph->fsphAttrMatch(ulAttribute, finfo->attr & 0x37) || !ph->fsphWildMatch(mask, name, ND_IGNORE_CASE))) return 0; stat.cbFile = finfo->size; stat.cbFileAlloc = stat.cbFile; stat.attrFile = (finfo->attr & 0x37); fsphUnixTimeToDosDate(pRes, finfo->mtime, &stat.fdateLastWrite, &stat.ftimeLastWrite); fsphUnixTimeToDosDate(pRes, finfo->ctime, &stat.fdateCreation, &stat.ftimeCreation); fsphUnixTimeToDosDate(pRes, finfo->atime, &stat.fdateLastAccess, &stat.ftimeLastAccess); debuglocal(pRes, 9, "fname %s\n", finfo->fname); debuglocal(pRes, 9, "mtime %d %s", finfo->mtime, ctime( (time_t*)&finfo->mtime)); debuglocal(pRes, 9, "ftimeLastAccess %02d:%02d:%02d\n", stat.ftimeLastWrite.hours, stat.ftimeLastWrite.minutes, stat.ftimeLastWrite.twosecs*2); ph->fsphAddFile32L(plist, &stat, name, strlen(name), finfo, sizeof(*finfo), 0); return 1; } // // maps smbwrp_fileinfo data fields into a native OS/2 file informations // and add the result to Netdrive internal list // void addFindInfoCachedL(void *plist, void* custom) { smbwrp_fileinfo* finfo = (smbwrp_fileinfo*)custom; debuglocal(NULL, 9, "addFindInfoCachedL finfo %p, fname %s\n", finfo, finfo->fname); // convert STATUS fields FILESTATUS3L stat = {0}; stat.cbFile = finfo->size; stat.cbFileAlloc = stat.cbFile; stat.attrFile = (finfo->attr & 0x37); // convert timestamps fsphUnixTimeToDosDate(NULL, finfo->mtime, &stat.fdateLastWrite, &stat.ftimeLastWrite); fsphUnixTimeToDosDate(NULL, finfo->ctime, &stat.fdateCreation, &stat.ftimeCreation); fsphUnixTimeToDosDate(NULL, finfo->atime, &stat.fdateLastAccess, &stat.ftimeLastAccess); // add to netdrive plist ph->fsphAddFile32L(plist, &stat, finfo->fname, strlen(finfo->fname), finfo, sizeof(smbwrp_fileinfo), 0); } static unsigned char fromhex (char c) { if ('0' <= c && c <= '9') return c - '0'; if ('A' <= c && c <= 'F') return c - 'A' + 0xA; if ('a' <= c && c <= 'f') return c - 'a' + 0xA; return 0; } static char tohex (unsigned char b) { b &= 0xF; if (b <= 9) return b + '0'; return 'A' + (b - 0xA); } static void decryptPassword (const char *pszCrypt, char *pszPlain) { // A simple "decryption", character from the hex value. const char *s = pszCrypt; char *d = pszPlain; while (*s) { *d++ = (char)((fromhex (*s++) << 4) + fromhex (*s++)); } *d++ = 0; return; } static void encryptPassword (const char *pszPlain, char *pszCrypt) { // A simple "encryption" encode each character as hex value. */ const char *s = pszPlain; char *d = pszCrypt; while (*s) { *d++ = tohex ((*s) >> 4); *d++ = tohex (*s); s++; } *d++ = 0; return; } /* callback for caching code used to free resources allocated when * calling dircache_write_entry() in smbwrp.c */ static void cacheFreeEntry( void*custom) { smbwrp_fileinfo* finfo = (smbwrp_fileinfo*)custom; debuglocal(NULL, 9, "cacheFreeEntry finfo %p -> %s\n", finfo, finfo->fname); // release memory free( finfo); } /* accept parameters in form * [filename][;name=filename] */ int initResource (Resource *pRes, NDPROPERTYHANDLE properties) { int rc = NO_ERROR; unsigned long t; const char * q = NULL; int defaultPassword = 1; pRes->rootlevel = 0; pRes->easupport = 1; #ifdef HAVE_KRB5_H pRes->krb5support = 1; #else pRes->krb5support = 0; #endif pRes->loglevel = 0; t = 0, q = NULL; rc = ph->fsphQueryStringProperty (properties, "WORKGROUP", &q, &t); if (!rc && t && *q) { strncpy(pRes->srv.workgroup, q, sizeof(pRes->srv.workgroup) - 1); pRes->rootlevel = 1; } t = 0, q = NULL; rc = ph->fsphQueryStringProperty (properties, "SERVER", &q, &t); if (!rc && t && *q) { strncpy(pRes->srv.server_name, q, sizeof(pRes->srv.server_name) - 1); pRes->rootlevel = 2; } t = 0, q = NULL; rc = ph->fsphQueryStringProperty (properties, "SHARE", &q, &t); if (!rc && t && *q) { strncpy(pRes->srv.share_name, q, sizeof(pRes->srv.share_name) - 1); pRes->rootlevel = 3; } t = 0, q = NULL; rc = ph->fsphQueryStringProperty (properties, "USER", &q, &t); if (!rc && t && *q) strncpy(pRes->srv.username, q, sizeof(pRes->srv.username) - 1); t = 0, q = NULL; rc = ph->fsphQueryStringProperty (properties, "PASSWORD", &q, &t); if (!rc && t && *q) { strncpy(pRes->srv.password, q, sizeof(pRes->srv.password) - 1); defaultPassword = 0; } t = 0, q = NULL; rc = ph->fsphQueryStringProperty (properties, "SPASSWORD", &q, &t); if (rc == NO_ERROR && *q != '\0' && defaultPassword) { char p[1024]; p[0] = 0; decryptPassword (q, p); if (*p) { strncpy(pRes->srv.password, p, sizeof(pRes->srv.password) - 1); // clear the plain password ph->fsphSetProperty (properties, "PASSWORD", ""); } } else { char c[1024]; encryptPassword (pRes->srv.password, c); ph->fsphSetProperty (properties, "SPASSWORD", c); // clear the plain password ph->fsphSetProperty (properties, "PASSWORD", ""); } t = 0, q = NULL; rc = ph->fsphQueryStringProperty (properties, "MASTER", &q, &t); if (!rc && t && *q) strncpy(pRes->srv.master, q, sizeof(pRes->srv.master) - 1); t = 0; rc = ph->fsphQueryUlongProperty (properties, "MASTERTYPE", &t); if (!rc) { if (t > 1) rc = ERROR_INVALID_PARAMETER; else pRes->srv.ifmastergroup = t; } t = 0; rc = ph->fsphQueryUlongProperty (properties, "EASUPPORT", &t); if (!rc) { if (t > 1) rc = ERROR_INVALID_PARAMETER; else pRes->easupport = t; } t = 0; rc = ph->fsphQueryUlongProperty (properties, "CTO", &t); if (!rc) { if (t > 600) rc = ERROR_INVALID_PARAMETER; else pRes->cachetimeout = t; } t = 0; rc = ph->fsphQueryUlongProperty (properties, "CLD", &t); if (!rc) { if (t > 96) rc = ERROR_INVALID_PARAMETER; else pRes->cachedepth = t; } t = 0; rc = ph->fsphQueryUlongProperty (properties, "LOGLEVEL", &t); if (!rc) { if (t > 10) rc = ERROR_INVALID_PARAMETER; else pRes->loglevel = t; } // figure out the used share/server/workgroup char *logfileext = '\0'; if (pRes->srv.share_name) logfileext = pRes->srv.share_name; if (!logfileext && pRes->srv.server_name) logfileext = pRes->srv.server_name; if (!logfileext && pRes->srv.workgroup) logfileext = pRes->srv.workgroup; // init debug and write the very first message debugInit(pRes, logfileext, ifL); debuglocal(pRes, 9, "NdpMountResource initResource\n"); // init code smbwrp_init(pRes); /* * Create a directory cache with expiration time and cache listings * the above values come from the gui. default: timeout 10; listings: 32 */ dircache_create(&pRes->pdc, (void*) pRes, pRes->cachetimeout, pRes->cachedepth, cacheFreeEntry, ph); return rc; } int iftestpath(char * path) { char * p = path; if (!path) return 0; while ((p = ph->fsphStrChr(p, 'A')) != NULL) { if (ph->fsphStrNCmp(p, "A.+,;=[].B", 10) == 0) return 1; p++; } return 0; } int pathparser(Resource *pRes, Connection * pConn, char * path, char * result) { int rootlevel; int rc = NO_ERROR; if (!pRes || !path || !result) return ERROR_INVALID_PARAMETER; /* handle special case when someone wants to test support of * LFN or smth similar */ if (iftestpath(path)) { strcpy(result, "\\A.+,;=[].B"); return NO_ERROR; } rootlevel = pRes->rootlevel; if (*path == '\\') path++; if (rootlevel < 3) { char * p; // flag: 1 parameters changed, reconnection required, 0 do nothing int newlevel = 0; // use a temporary resource to test disconnection/reconnection Resource tmpRes; // copy exising data memcpy( &tmpRes, pRes, sizeof( tmpRes)); // pointer to new connection fields smbwrp_server * tmp = &tmpRes.srv; if (rootlevel == 0) { p = ph->fsphStrChr(path, '\\'); if (!p) p = path + strlen(path); if (strlen(tmp->workgroup) != p - path || (p == path || ph->fsphStrNICmp(path, tmp->workgroup, p - path))) { strncpy(tmp->workgroup, path, p - path); tmp->workgroup[p - path] = 0; newlevel = 1; } path = *p == '\\' ? p + 1 : p; rootlevel = 1; } if (rootlevel == 1) // root path starts from server name { p = ph->fsphStrChr(path, '\\'); if (!p) p = path + strlen(path); if (strlen(tmp->server_name) != p - path || (p == path || ph->fsphStrNICmp(path, tmp->server_name, p - path))) { strncpy(tmp->server_name, path, p - path); tmp->server_name[p - path] = 0; newlevel = 1; } path = *p == '\\' ? p + 1 : p; rootlevel = 2; } if (rootlevel == 2) // root path starts from share name { p = ph->fsphStrChr(path, '\\'); if (!p) p = path + strlen(path); if (strlen(tmp->share_name) != (p - path) || (p == path || ph->fsphStrNICmp(path, tmp->share_name, p - path))) { strncpy(tmp->share_name, path, p - path); tmp->share_name[p - path] = 0; newlevel = 1; } path = *p == '\\' ? p + 1 : p; } if (newlevel) { // reconnect to server here, first test new connection cli_state* tmp_cli = NULL; rc = smbwrp_connect(&tmpRes, &tmp_cli); if (!rc) { // new connection is ok, disconnect old one cli_state* cli = pConn->cli; smbwrp_disconnect(pRes, cli); // save tmp data structure memcpy( pRes, &tmpRes, sizeof( tmpRes)); // save new connection handle pConn->cli = tmp_cli; } } } strcpy(result, "\\"); strncat(result, path, CCHMAXPATH); return rc; } /* check if the requested resource is available */ static int checkMountResource(Resource* pRes) { int rc; unsigned long action; cli_state* cli = NULL; debuglocal(pRes, 9, "checkMountResource in tid#%d\n", _gettid()); rc = smbwrp_connect(pRes, &cli); switch (rc) { case 0: rc = NO_ERROR; break; case 1: case 10: case 11: rc = ERROR_BAD_NET_NAME; break; case 2: rc = ERROR_INIT_ROUTINE_FAILED; break; case 3: rc = ERROR_BAD_NET_RESP; break; case 4: rc = ERROR_NETWORK_BUSY; break; case 6: rc = ERROR_NETWORK_ACCESS_DENIED; break; case 7: rc = ERROR_BAD_NETPATH; break; default: rc = ERROR_UNEXP_NET_ERR; break; } /* endswitch */ smbwrp_disconnect(pRes, cli); return rc; } int APIENTRY NdpMountResource (HRESOURCE *presource, int type, NDPROPERTYHANDLE properties) { int rc = NO_ERROR; unsigned long objany = OBJ_ANY; Resource *pRes = NULL; ENTER(); /* * since samba plugin support only 1 type of resources we do not need * to check what the found type really is */ pRes = malloc( sizeof(Resource)); if (pRes == NULL) rc = ERROR_NOT_ENOUGH_MEMORY; else { memset(pRes, 0, sizeof(Resource)); // parse init string rc = initResource (pRes, properties); /* * try to connect to resource (check type) only if thread!=1, * so ndctl startup is not slowed down by network connections. * ndctl does mounting on main thread (#1) * nd/ndpm do not use main thread */ if (!rc && _gettid()!=1) rc = checkMountResource( pRes); if (!rc) { // store resource data *presource = (HRESOURCE)pRes; } else free(pRes); } debuglocal(pRes, 9, "NdpMountResource rc=%d\n", rc); LEAVE(); return rc; } int APIENTRY NdpFreeResource (HRESOURCE resource) { Resource *pRes = (Resource *)resource; ENTER(); // free cache dir dircache_delete(pRes->pdc); // free local resources memset(&pRes->srv, 0, sizeof(pRes->srv)); debuglocal(pRes, 9, "NdpFreeResource %d\n", NO_ERROR); debugClose(pRes); free(pRes); LEAVE(); return NO_ERROR; } int APIENTRY NdpRsrcCompare (HRESOURCE resource, HRESOURCE resource2) { Resource *pRes = (Resource *)resource; Resource *pRes2 = (Resource *)resource2; int rc = ND_RSRC_DIFFERENT; debuglocal(pRes, 9, "NdpRsrcCompare in\n"); if (ph->fsphStrICmp(pRes->srv.server_name, pRes2->srv.server_name) == 0 && ph->fsphStrICmp(pRes->srv.share_name, pRes2->srv.share_name) == 0 && ph->fsphStrICmp(pRes->srv.username, pRes2->srv.username) == 0 && ph->fsphStrICmp(pRes->srv.workgroup, pRes2->srv.workgroup) == 0) { // resources are equal rc = ND_RSRC_EQUAL; } debuglocal(pRes, 9, "NdpRsrcCompare %d\n", rc); return rc; } int APIENTRY NdpRsrcUpdate (HRESOURCE resource, HRESOURCE resource2) { // do nothing Resource *pRes = (Resource *)resource; debuglocal(pRes, 9, "NdpRsrcUpdate %d\n", NO_ERROR); return NO_ERROR; } int APIENTRY NdpRsrcQueryInfo (HRESOURCE resource, ULONG *pulFlags, void *pdata, ULONG insize, ULONG *poutlen) { Resource *pRes = (Resource *)resource; int rc = NO_ERROR; char s[4096]; debuglocal(pRes, 9, "NdpRsrcQueryInfo in\n"); switch (pRes->rootlevel) { case 0: ph->fsph_snprintf(s, sizeof(s) - 1, "SMBFS%s \\\\@%s", ifL ? "64" : "32", pRes->srv.username); break; case 1: ph->fsph_snprintf(s, sizeof(s) - 1, "SMBFS%s %s: \\\\@%s", ifL ? "64" : "32", pRes->srv.workgroup, pRes->srv.username); break; case 2: ph->fsph_snprintf(s, sizeof(s) - 1, "SMBFS%s \\\\%s%s%s@%s", ifL ? "64" : "32", *pRes->srv.workgroup ? pRes->srv.workgroup : "", *pRes->srv.workgroup ? ":" : "", pRes->srv.server_name, pRes->srv.username); break; default: ph->fsph_snprintf(s, sizeof(s) - 1, "SMBFS%s \\\\%s%s%s\\%s@%s", ifL ? "64" : "32", *pRes->srv.workgroup ? pRes->srv.workgroup : "", *pRes->srv.workgroup ? ":" : "", pRes->srv.server_name, pRes->srv.share_name, pRes->srv.username); break; } *poutlen = strlen(s) + 1; if (*poutlen > insize) rc = ERROR_BUFFER_OVERFLOW; else memcpy(pdata, s, *poutlen); debuglocal(pRes, 9, "NdpRsrcQueryInfo %d\n", rc); return rc; } int APIENTRY NdpRsrcQueryFSAttach (HRESOURCE resource, void *pdata, ULONG insize, ULONG *poutlen) { ULONG ulDummy = 0; // just return the resource info string return NdpRsrcQueryInfo (resource, &ulDummy, pdata, insize, poutlen); } int APIENTRY NdpRsrcQueryFSAllocate (HRESOURCE resource, NDFSALLOCATE *pfsa) { Resource *pRes = (Resource *)resource; int rc = NO_ERROR, rc1; unsigned long action = 0; cli_state* cli = NULL; FSALLOCATE fsa; ENTER(); debuglocal(pRes, 9, "NdpRsrcQueryFSAllocate %08x\n", pfsa); if (!pfsa) { LEAVE(); return NO_ERROR; } debuglocal(pRes, 9, "NdpRsrcQueryFSAllocate in tid#%d\n", _gettid()); rc = smbwrp_connect(pRes, &cli); if (rc) { debuglocal(pRes, 9, "NdpRsrcQueryFSAllocate smbwrp_connect failed rc=%d\n", rc); pfsa->cSectorUnit = 1; pfsa->cUnit = 123456; pfsa->cUnitAvail = 123456; pfsa->cbSector = 2048; rc = (rc == 7 ? ERROR_BAD_DEV_TYPE : ERROR_ACCESS_DENIED); LEAVE(); return rc; } rc = smbwrp_dskattr(pRes, cli, &fsa); if (rc) { pfsa->cSectorUnit = 1; pfsa->cUnit = 123456; pfsa->cUnitAvail = 123456; pfsa->cbSector = 2048; //rc = rc ? rc : (resp.rc ? resp.rc : ERROR_INVALID_PARAMETER); } else { pfsa->cSectorUnit = fsa.cSectorUnit; pfsa->cUnit = fsa.cUnit; pfsa->cUnitAvail = fsa.cUnitAvail; pfsa->cbSector = fsa.cbSector; } smbwrp_disconnect(pRes, cli); debuglocal(pRes, 9, "NdpRsrcQueryFSAllocate %d/%d (cUnit = %d/cUnitAvail = %d/cbSector = %d)\n", rc, rc1, pfsa->cUnit, pfsa->cUnitAvail, pfsa->cbSector); LEAVE(); return rc; } int APIENTRY NdpCreateConnection (HRESOURCE resource, HCONNECTION *pconn) { int rc = 0; Resource * pRes = (Resource *)resource; unsigned long action; Connection *pConn = NULL; ENTER(); debuglocal(pRes, 9, "NdpCreateConnection in\n"); pConn = malloc( sizeof(Connection)); if (pConn == NULL) rc = ERROR_NOT_ENOUGH_MEMORY; if (rc) { debuglocal(pRes, 9, "NdpCreateConnection ERROR_NOT_ENOUGH_MEMORY %d\n", rc); LEAVE(); return rc; } memset(pConn, 0, sizeof(Connection)); pConn->pRes = pRes; pConn->file.fd = -1; debuglocal(pRes, 9, "NdpCreateConnection send CONNECT\n"); rc = smbwrp_connect(pRes, &pConn->cli); if (rc) { free(pConn); pConn = NULL; rc = (rc == 7 ? ERROR_BAD_DEV_TYPE : ERROR_INVALID_PARAMETER); } *pconn = (HCONNECTION)pConn; debuglocal(pRes, 9, "NdpCreateConnection [%p] %d\n", pConn, rc); LEAVE(); return rc; } int APIENTRY NdpFreeConnection (HCONNECTION conn) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc; ENTER(); debuglocal(pRes, 9, "NdpFreeConnection in [%p]\n", pConn); if (pConn->file.fd >= 0) { rc = smbwrp_close(pRes, pConn->cli, &pConn->file); pConn->file.fd = -1; } smbwrp_disconnect(pRes, pConn->cli); debuglocal(pRes, 9, "NdpFreeConnection %d\n", NO_ERROR); free(pConn); LEAVE(); return NO_ERROR; } /* * NdpQueryPathInfo is the most important function :) netdrive always calls * the function before every operation to find out the path status: * does it exist, is it a file, does a parent directory exist, etc. * Plugin must return one of the following error codes: * NO_ERROR - path exists and the path information have been successfully * retrieved. * ERROR_FILE_NOT_FOUND - all but the last component of the path exist and the * path without the last component is a directory. dir1_ok\dir2_ok\does_not_exist. * the wildcard can not exist, so the plugin returns FILE_NOT_FOUND, if the parent * directory exist. * ERROR_PATH_NOT_FOUND - any of not last path components does not exist, or all * but the last component exist and is a file: \dir_ok\dir2_ok\file_ok\non_existing. * ERROR_REM_NOT_LIST - resource is temporarily unavailable for some reasons. * Any other error codes means an internal plugin error, not related to the status * of the path queried. */ int APIENTRY NdpQueryPathInfo (HCONNECTION conn, void *plist, char *szPath) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; smbwrp_fileinfo finfo; int rc = 0; int rcCon = 0; char path[CCHMAXPATH+1] = {0}; NDPATHELEMENT *pel = NULL; ENTER(); debuglocal(pRes, 9, "NdpQueryPathInfo in [%p] <%s>\n", pConn, szPath); /* * if wildcard is specified, we suppose parent dir exist, * so exit immediately */ if (ph->fsphStrChr(szPath, '*') || ph->fsphStrChr(szPath, '?')) { LEAVE(); return ERROR_FILE_NOT_FOUND; } do { // First check if there is information in the directory cache. unsigned long ulAge = 0; void* _finfo; if (dircache_find_path(pRes->pdc, szPath, &_finfo, &ulAge)) { if (ulAge <= 15) /* @todo configurable. */ { rc = NO_ERROR; ((smbwrp_fileinfo*)_finfo)->easize = -1; addFindInfoCachedL(plist, _finfo); break; } } rc = pathparser(pRes, pConn, szPath, path); debuglocal(pRes, 9, "NdpQueryPathInfo pathparser for <%s> rc=%d\n", path, rc); switch (rc) { case NO_ERROR : case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_ACCESS_DENIED: case ERROR_INVALID_PARAMETER: break; default : rc = ERROR_PATH_NOT_FOUND; } if (rc) break; strncpy(finfo.fname, path, sizeof(finfo.fname) - 1); debuglocal(pRes, 9, "NdpQueryPathInfo smbwrp_getattr for <%s>\n", path); rc = smbwrp_getattr(pRes, &pRes->srv, pConn->cli, &finfo); if (rc) { // remote server not available for first time? if (rc == ERROR_REM_NOT_LIST) { // free current cli resources smbwrp_disconnect(pRes, pConn->cli); // reconnect rcCon = smbwrp_connect(pRes, &pConn->cli); if (rcCon != NO_ERROR) debuglocal(pRes, 9, "NdpQueryPathInfo smbwrp_connect rc = %d\n", rcCon); // try file list again if reconnecting worked if (rcCon == NO_ERROR) rc = smbwrp_getattr(pRes, &pRes->srv, pConn->cli, &finfo); } debuglocal(pRes, 9, "NdpQueryPathInfo smbwrp_getattr, rc = %d\n", rc); switch (rc) { case NO_ERROR : case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_ACCESS_DENIED: case ERROR_INVALID_PARAMETER: case ERROR_REM_NOT_LIST: break; default : rc = ERROR_PATH_NOT_FOUND; } } if (rc == NO_ERROR) { finfo.easize = -1; addFindInfoL(pRes, plist, &finfo, 0, NULL); } else if (rc == ERROR_FILE_NOT_FOUND) { // now try the upper path char * p = ph->fsphStrRChr(finfo.fname, '\\'); if (p && p > finfo.fname) { *p = 0; rc = smbwrp_getattr(pRes, &pRes->srv, pConn->cli, &finfo); debuglocal(pRes, 9, "NdpQueryPathInfo upper path in <%s>, rc = %d\n", finfo.fname, rc); if (rc == NO_ERROR) rc = (finfo.attr & FILE_DIRECTORY) !=0 ? ERROR_FILE_NOT_FOUND:ERROR_PATH_NOT_FOUND; else if (rc != ERROR_REM_NOT_LIST) rc = ERROR_PATH_NOT_FOUND; } } } while (0); // if file/path exists, store parent path in resourcedata // for NdpEA* calls if ((rc == NO_ERROR || rc == ERROR_FILE_NOT_FOUND) && (pel = ph->fsphNameElem (0))) { char szFullPath[CCHMAXPATH+1] = {0}; // save parent full path for future use in NdpEA* calls strcpy(szFullPath, szPath); // strip pel name, leaving ending slash in place int len = strlen(szFullPath) - pel->length; szFullPath[len] = '\0'; int rc2 = ph->fsphSetResourceData( plist, szFullPath, strlen(szFullPath) + 1); debuglocal(pRes, 9, "NdpQueryPathInfo <%s> plist %x, szFullPath (%s), rc2=%d\n", szPath, plist, szFullPath, rc2); } debuglocal(pRes, 9, "NdpQueryPathInfo <%s> (%s) %d\n", szPath, path, rc); LEAVE(); return rc; } int APIENTRY NdpFindStart (HCONNECTION conn, void *plist, NDFILEINFOL *pfiparent, char *szPath, ULONG ulAttribute) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = NO_ERROR; unsigned long action; char *mask = "*"; char dir[CCHMAXPATH+1] = {0}; char path[CCHMAXPATH+1] = {0}; NDPATHELEMENT *pel = ph->fsphNameElem(0); filelist_state state; char * p; ENTER(); debuglocal(pRes, 9, "NdpFindStart in [%p]\n", pConn); strncpy(dir, szPath, sizeof(dir) - 1); if (pel) { mask = pel->name; dir[strlen(szPath) - pel->length] = 0; } action = strlen(dir) - 1; if (dir[action] == '\\') dir[action] = 0; rc = pathparser(pRes, pConn, dir, path); if (rc) return rc; action = strlen(path) - 1; if (path[action] != '\\') strncat(path, "\\", sizeof(path) - 1); strcpy(dir, path); strncat(path, mask, sizeof(path) - 1); /* * this structure will be used by libsmb callbacks, * so we store here all we need to fill netdrive structures */ state.pConn = pConn; state.plist = plist; state.ulAttribute = ulAttribute; strcpy(state.dir, dir); strcpy(state.dir_mask, mask); strcpy(state.mask, path); state.fullpath = szPath; /* * This plugin always reads a complete directory listing and * filters results using actual mask (state.dir_mask) in addFindInfoL. * May be this was a workaround for some server bug. * If this will be changed, then directory listing cache must * be changed too, and must remember the mask, which was used to * obtain a listing. Now the directory cache saves complete directory * listings and then uses them to find information about single files. * However, with a directory cache, it is probably faster to get a full * listing and then use it to obtain info about separate files than to * perform a network list query operation using actual wild cards for * each file. Some application, for example OpenOffice, do this. */ p = getlastslash(state.mask); if (p) { *(p + 1) = '*'; *(p + 2) = 0; } else strcpy(state.mask, "\\*"); debuglocal(pRes, 9, "NdpFindStart: dir [%s], dir_mask [%s], mask [%s], szPath [%s]\n", state.dir, state.dir_mask, state.mask, state.fullpath); rc = smbwrp_filelist(pRes, &pRes->srv, pConn->cli, &state); /* * we need to handle reconnection also here, because NdpQueryPathInfo * could be called with '*' and exit then immediately * (without calling libsmb) */ if (rc == ERROR_REM_NOT_LIST) { // free current cli resources smbwrp_disconnect(pRes, pConn->cli); // reconnect smbwrp_connect(pRes, &pConn->cli); // try file list again next loop rc = smbwrp_filelist(pRes, &pRes->srv, pConn->cli, &state); debuglocal(pRes, 9, "NdpFindStart remote connection lost, rc = %d\n", rc); } debuglocal(pRes, 9, "NdpFindStart <%s> (%s) rc %d\n", szPath, path, rc); LEAVE(); return rc; } int APIENTRY NdpDeletePathInfo (HRESOURCE resource, NDFILEINFOL *pfi) { Resource * pRes = (Resource *)resource; debuglocal(pRes, 9, "NdpDeletePathInfo %d\n", 0); return NO_ERROR; } int APIENTRY NdpRefresh (HCONNECTION conn, char *path, int tree) { Connection *pConn = (Connection *)conn; debuglocal(pConn->pRes, 9, "NdpRefresh <%s> %d\n", path, 0); return NO_ERROR; } int APIENTRY NdpDiscardResourceData (HRESOURCE resource, NDDATABUF *pdatabuf) { /* * The plugin do not have to deallocate anything, because * resource data did not contain any pointers to plugins data. * Data stored by fsphSetResourceData will be deallocated by NetDrive. */ Resource * pRes = (Resource *)resource; debuglocal(pRes, 9, "NdpDicardresourceData %d\n", 0); return NO_ERROR; } int APIENTRY NdpSetPathInfo (HCONNECTION conn, NDFILEINFOL *pfi, char *szPathName) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; char path[CCHMAXPATH+1] = {0}; smbwrp_fileinfo finfo; ENTER(); debuglocal(pRes, 9, "NdpSetPathInfo in [%p]\n", pConn); // delete the dir cache dircache_invalidate(pRes->pdc, szPathName, 1); do { rc = pathparser(pRes, pConn, szPathName, path); if (rc) break; memset(&finfo, 0, sizeof(finfo)); strncpy(finfo.fname, path, sizeof(finfo.fname) - 1); fsphDosDateToUnixTime(pRes, pfi->stat.fdateLastWrite, pfi->stat.ftimeLastWrite, &(finfo.mtime)); finfo.attr = pfi->stat.attrFile & 0x37; rc = smbwrp_setattr(pRes, pConn->cli, &finfo); } while (0); debuglocal(pRes, 9, "NdpSetPathInfo <%s> (%s) %d\n", szPathName, path, rc); LEAVE(); return rc; } int buildFEALIST(Resource *pRes, FEALIST *pFEASrc, GEALIST *pGEAList, FEALIST *pFEAList) { int rc = 0; FEA * pfea; FEA * pfeadest; unsigned long size; unsigned long done = sizeof(pFEAList->cbList); unsigned long dsize; unsigned long ddone = sizeof(pFEAList->cbList); size = pFEASrc->cbList; pfea = pFEASrc->list; pfeadest = pFEAList->list; dsize = pFEAList->cbList; //debuglocal(pRes, 9,"buildFEALIST in destsize %d srcsize %d pGEAList=%08x pGEAList->cbList=%d\n", dsize, ddone, size, pGEAList, pGEAList ? pGEAList->cbList : 0); while (done < size) { char * name = (char *)(pfea + 1); int insert = 1; if (pGEAList && pGEAList->cbList > sizeof(pGEAList->cbList)) { GEA * pgea = pGEAList->list; unsigned long size = pGEAList->cbList - sizeof(pGEAList->cbList); unsigned long done = 0; insert = 0; while (done < size) { //debuglocal(pRes, 9,"comp <%s> <%s>\n", name, pgea->szName); if (!ph->fsphStrNCmp(name, pgea->szName, pgea->cbName)) { insert = 1; break; } done += pgea->cbName + 2; pgea = (GEA *)((char *)pgea + pgea->cbName + 2); } } if (insert) { ddone += sizeof(FEA) + pfea->cbName + 1 + pfea->cbValue; if (ddone <= dsize) { pfeadest->cbName = pfea->cbName; pfeadest->cbValue = pfea->cbValue; pfeadest->fEA = 0; strcpy((char *)(pfeadest + 1), name); memcpy((char *)(pfeadest + 1) + pfea->cbName + 1, (char *)(pfea + 1) + pfea->cbName + 1, pfea->cbValue); pfeadest = (FEA *)((char *)pFEAList + ddone); } } done += sizeof(FEA) + pfea->cbName + 1 + pfea->cbValue; //debuglocal(pRes, 9,"buuildfea <%s> insert=%d pfea->cbName=%d pfea->cbValue=%d srcdone=%d destdone=%d pfeadest=%08x pfea=%08x\n", name, insert, pfea->cbName, pfea->cbValue, done, ddone, pfeadest, pfea); pfea = (FEA *)((char *)pFEASrc + done); } pFEAList->cbList = ddone; if (ddone > dsize && dsize > sizeof(pFEAList->cbList)) rc = ERROR_BUFFER_OVERFLOW; debuglocal(pRes, 9, "buildFEALIST rc=%d destsize=%d destdone=%d srcsize=%d pGEAList=%08x\n", rc, dsize, ddone, size, pGEAList); return rc; } int APIENTRY NdpEAQuery (HCONNECTION conn, GEALIST *pGEAList, NDFILEINFOL *pfi, FEALIST *pFEAList) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; FEALIST * pFEASrc; NDDATABUF fdata = {0}; const int cbBuffer = 64*1024; NDDATABUF rsrcData = {0}; char szFullPath[CCHMAXPATH+1]; if (!pfi || !pfi->pszName || !pFEAList) return ERROR_EAS_NOT_SUPPORTED; if (!pRes->easupport) return ERROR_EAS_NOT_SUPPORTED; // get full path stored in resource data rc = ph->fsphGetResourceData(pfi, &rsrcData, 0); if (rc != NO_ERROR || rsrcData.ulSize == 0 || rsrcData.pData == NULL) { debuglocal(pRes, 9, "NdpEAQuery: ph->fsphGetResourceData = %d/%d %08x\n", rc, fdata.ulSize, fdata.pData); return ERROR_EAS_NOT_SUPPORTED; } ENTER(); // build full path for file buildfullpath2(szFullPath, &rsrcData, pfi); debuglocal(pRes, 1, "NdpEAQuery szFullPath '%s', pfi->name '%s'\n", szFullPath, pfi->pszName); debuglocal(pRes, 9, "NdpEAQuery in [%p] <%s> %08x %d\n", pConn, szFullPath, pGEAList, pGEAList ? pGEAList->cbList : 0); char *pchBuffer = (char *)malloc(cbBuffer); if (!pchBuffer) { LEAVE(); return ERROR_NOT_ENOUGH_MEMORY; } do { rc = smbwrp_listea(pRes, pConn->cli, szFullPath, pchBuffer, cbBuffer); pFEASrc = (FEALIST*) pchBuffer; if (rc) { switch (rc) { case ERROR_FILE_NOT_FOUND : case ERROR_PATH_NOT_FOUND : pFEAList->cbList = sizeof(pFEAList->cbList); rc = NO_ERROR; break; case ERROR_BUFFER_OVERFLOW : pFEAList->cbList = pFEASrc->cbList; break; default : rc = ERROR_EAS_NOT_SUPPORTED; } } else rc = buildFEALIST(pRes, (FEALIST *)pFEASrc, pGEAList, pFEAList); } while (0); free(pchBuffer); debuglocal(pRes, 9, "NdpEAQuery <%s> %d %d %d\n", pfi->pszName, rc, pFEASrc->cbList, pFEAList->cbList); LEAVE(); return rc; } int APIENTRY NdpEASet (HCONNECTION conn, FEALIST *pFEAList, NDFILEINFOL *pfi) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; NDDATABUF fdata = {0}; NDDATABUF rsrcData = {0}; char szFullPath[CCHMAXPATH+1]; debuglocal(pRes, 9, "NdpEASet in [%p]\n", pConn); if (!pfi || !pfi->pszName) return ERROR_EAS_NOT_SUPPORTED; if (!pRes->easupport) return ERROR_EAS_NOT_SUPPORTED; // get full path stored in resource data rc = ph->fsphGetResourceData(pfi, &rsrcData, 0); if (rc != NO_ERROR || rsrcData.ulSize == 0 || rsrcData.pData == NULL) { debuglocal(pRes, 9, "NdpEASet: ph->fsphGetResourceData = %d/%d %08x\n", rc, fdata.ulSize, fdata.pData); return ERROR_EAS_NOT_SUPPORTED; } // build full path for file buildfullpath2(szFullPath, &rsrcData, pfi); debuglocal(pRes, 9, "NdpEASet szFullPath '%s', pfi->name '%s'\n", szFullPath, pfi->pszName); rc = helperEASet(pRes, pConn->cli, pFEAList, szFullPath); debuglocal(pRes, 9, "NdpEASet %d\n", rc); return rc; } int APIENTRY NdpEASize (HCONNECTION conn, NDFILEINFOL *pfi, ULONG *pulEASize) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; FEALIST * pfealist; NDDATABUF fdata = {0}; const int cbBuffer = 64*1024; NDDATABUF rsrcData = {0}; char szFullPath[CCHMAXPATH+1]; if (!pfi || !pulEASize) return ERROR_EAS_NOT_SUPPORTED; if (!pRes->easupport) return ERROR_EAS_NOT_SUPPORTED; // get full path stored in resource data rc = ph->fsphGetResourceData(pfi, &rsrcData, 0); if (rc != NO_ERROR || rsrcData.ulSize == 0 || rsrcData.pData == NULL) { debuglocal(pRes, 9, "NdpEASize: ph->fsphGetResourceData = %d/%d %08x\n", rc, fdata.ulSize, fdata.pData); return ERROR_EAS_NOT_SUPPORTED; } ENTER(); // build full path for file buildfullpath2(szFullPath, &rsrcData, pfi); debuglocal(pRes, 1, "NdpEASize szFullPath '%s', pfi->name '%s'\n", szFullPath, pfi->pszName); // @todo restore use of easize from cache debuglocal(pRes, 9, "NdpEASize in [%p] <%s> \n", pConn, szFullPath); char *pchBuffer = (char *)malloc(cbBuffer); if (!pchBuffer) { LEAVE(); return ERROR_NOT_ENOUGH_MEMORY; } do { rc = smbwrp_listea(pRes, pConn->cli, szFullPath, pchBuffer, cbBuffer); pfealist = (FEALIST*)pchBuffer; if (rc) { switch (rc) { case ERROR_FILE_NOT_FOUND : case ERROR_PATH_NOT_FOUND : pfealist->cbList = sizeof(pfealist->cbList); // Fall through */ case ERROR_BUFFER_OVERFLOW : rc = NO_ERROR; break; default : rc = ERROR_EAS_NOT_SUPPORTED; } } *pulEASize = pfealist->cbList; } while (0); free(pchBuffer); debuglocal(pRes, 9, "NdpEASize <%s> %d %d\n", pfi->pszName, *pulEASize, rc); LEAVE(); return rc; } int APIENTRY NdpSetCurrentDir (HCONNECTION conn, NDFILEINFOL *pfi, char *szPath) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; char path[CCHMAXPATH+1] = {0}; debuglocal(pRes, 9, "NdpSetCurrentDir in [%p]\n", pConn); ENTER(); do { rc = pathparser(pRes, pConn, szPath, path); if (rc) break; rc = smbwrp_chdir(pRes, &pRes->srv, pConn->cli, path); } while (0); debuglocal(pRes, 9, "NdpSetCurrentDir <%s> (%s) %d\n", szPath, path, rc); LEAVE(); return rc; } int APIENTRY NdpCopy (HCONNECTION conn, NDFILEINFOL *pfiDst, char *szDst, NDFILEINFOL *pfiSrc, char *szSrc, ULONG ulOption) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; debuglocal(pRes, 9, "NdpCopy <%s> -> <%s> %d\n", szSrc, szDst, ERROR_CANNOT_COPY); return ERROR_CANNOT_COPY; } int APIENTRY NdpCopy2 (HCONNECTION conn, HRESOURCE resDst, NDFILEINFOL *pfiDst, char *szDst, NDFILEINFOL *pfiSrc, char *szSrc, ULONG ulOption) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; debuglocal(pRes, 9, "NdpCopy2 <%s> -> <%s> %d\n", szSrc, szDst, ERROR_CANNOT_COPY); return ERROR_CANNOT_COPY; } int APIENTRY NdpForceDelete (HCONNECTION conn, NDFILEINFOL *pfi, char *szFile) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; char path[CCHMAXPATH+1] = {0}; ENTER(); debuglocal(pRes, 9, "NdpForceDelete in [%p]\n", pConn); dircache_invalidate(pRes->pdc, szFile, 1); do { rc = pathparser(pRes, pConn, szFile, path); if (rc) break; rc = smbwrp_unlink(pRes, pConn->cli, path); } while (0); debuglocal(pRes, 9, "NdpForceDelete <%s> (%s) %d\n", szFile, path, rc); LEAVE(); return rc; } int APIENTRY NdpCreateDir (HCONNECTION conn, NDFILEINFOL *pfiparent, char *szDirName, FEALIST *pFEAList) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; int rcEASet = 0; unsigned long action; char path[CCHMAXPATH+1] = {0}; ENTER(); debuglocal(pRes, 9, "NdpCreateDir in [%p]\n", pConn); dircache_invalidate(pRes->pdc, szDirName, 1); do { rc = pathparser(pRes, pConn, szDirName, path); if (rc) break; rc = smbwrp_mkdir(pRes, pConn->cli, path); } while (0); LEAVE(); if (path && pRes->easupport) rcEASet = helperEASet(pRes, pConn->cli, pFEAList, path); debuglocal(pRes, 9, "NdpCreateDir <%s> (%s) rc=%d, EASupport=%s rc=%d\n", szDirName, path, rc, pRes->easupport?"yes":"no", rcEASet); return rc; } int APIENTRY NdpDeleteDir (HCONNECTION conn, NDFILEINFOL *pfi, char *szDir) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; char path[CCHMAXPATH+1] = {0}; ENTER(); debuglocal(pRes, 9, "NdpDeleteDir in [%p]\n", pConn); dircache_invalidate(pRes->pdc, szDir, 1); do { rc = pathparser(pRes, pConn, szDir, path); if (rc) break; rc = smbwrp_rmdir(pRes, pConn->cli, path); } while (0); debuglocal(pRes, 9, "NdpDeleteDir <%s> (%s) %d\n", szDir, path, rc); LEAVE(); return rc; } int APIENTRY NdpMove (HCONNECTION conn, NDFILEINFOL *pfiDst, char *szDst, NDFILEINFOL *pfiSrc, char *szSrc) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; char src[CCHMAXPATH+1] = {0}; int l1, l2; char * p = szDst; ENTER(); debuglocal(pRes, 9, "NdpMove in [%p] from <%s> to <%s>\n", pConn, szSrc, szDst); dircache_invalidate(pRes->pdc, szSrc, 1); dircache_invalidate(pRes->pdc, szDst, 1); do { rc = pathparser(pRes, pConn, szSrc, src); if (rc) break; l1 = strlen(szSrc); l2 = strlen(src); if (l1 > l2) { if (ph->fsphStrNICmp(szSrc, szDst, l1 - l2)) { /* * the file moved accross different shares or servers or * workgroups */ rc = ERROR_WRITE_PROTECT; break; } p = szDst + l1 - l2 + 1; } rc = smbwrp_rename(pRes, pConn->cli, src, p); } while (0); debuglocal(pRes, 9, "NdpMove <%s> -> <%s> (%s) %d\n", szSrc, szDst, src, rc); LEAVE(); return rc; } int APIENTRY NdpMove2 (HCONNECTION conn, HRESOURCE resDst, NDFILEINFOL *pfiDst, char *szDst, NDFILEINFOL *pfiSrc, char *szSrc) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; debuglocal(pRes, 9, "NdpMove2 <%s> -> <%s> %d\n", szSrc, szDst, ERROR_WRITE_PROTECT); return ERROR_WRITE_PROTECT; } int APIENTRY NdpChangeCase (HCONNECTION conn, char *szDst, NDFILEINFOL *pfiSrc, char *szSrc, char *szNewName, ULONG ulNameLen) { return NdpMove (conn, pfiSrc, szDst, pfiSrc, szSrc); } int APIENTRY NdpRename (HCONNECTION conn, char *szDst, NDFILEINFOL *pfiSrc, char *szSrc, char *szNewName, ULONG ulNameLen) { return NdpMove (conn, pfiSrc, szDst, pfiSrc, szSrc); } int smbopen(Connection *pConn, char *szFileName, int flags, ULONG ulOpenMode, ULONG ulAttribute, FEALIST *pFEAList) { Resource *pRes = pConn->pRes; unsigned long action; int rc = 0; char path[CCHMAXPATH+1] = {0}; ENTER(); debuglocal(pRes, 9, "smbopen in [%p] %d\n", pConn, pConn->file.fd); if (flags & O_CREAT) dircache_invalidate(pRes->pdc, szFileName, 1); do { if (pConn->file.fd > 0) { rc = ERROR_TOO_MANY_OPEN_FILES; break; } rc = pathparser(pRes, pConn, szFileName, path); if (rc) break; strncpy(pConn->file.fullname, szFileName, sizeof(pConn->file.fullname) - 1); strncpy(pConn->file.fname, path, sizeof(pConn->file.fname) - 1); flags |= O_BINARY; switch (ulOpenMode & 3) { case OPEN_ACCESS_READONLY : flags |= O_RDONLY; break; case OPEN_ACCESS_WRITEONLY : flags |= O_WRONLY; break; case OPEN_ACCESS_READWRITE : flags |= O_RDWR; break; default : flags |= O_RDWR; } pConn->file.openmode = flags; pConn->file.openattr = ulAttribute & 0x37; pConn->file.denymode = (ulOpenMode & 0x70) >> 4; rc = smbwrp_open(pConn->pRes, pConn->cli, &pConn->file); } while (0); debuglocal(pRes, 9, "smbopen <%s> (%s) %08x %08x %08x %d. file = %d\n", szFileName, path, flags, ulOpenMode, ulAttribute, rc, pConn->file.fd); if (!rc && pFEAList) { int rc1 = NdpFileEASet((HCONNECTION)pConn, (NDFILEHANDLE)0, pFEAList); debuglocal(pRes, 9, "smbopen NdpFileEASet %d. pFEAList->cbList %d\n", rc1, pFEAList->cbList); } LEAVE(); return rc; } int APIENTRY NdpOpenReplace (HCONNECTION conn, NDFILEINFOL *pfi, NDFILEHANDLE *phandle, char *szFileName, ULONG ulSize, ULONG ulOpenMode, ULONG ulAttribute, FEALIST *pFEAList) { return smbopen((Connection *)conn, szFileName, O_TRUNC, ulOpenMode, ulAttribute, pFEAList); } int APIENTRY NdpOpenReplaceL(HCONNECTION conn, NDFILEINFO *pfi, NDFILEHANDLE *phandle, char *szFileName, LONGLONG llSize, ULONG ulOpenMode, ULONG ulAttribute, FEALIST *pFEAList) { return smbopen((Connection *)conn, szFileName, O_TRUNC, ulOpenMode, ulAttribute, pFEAList); } int APIENTRY NdpOpenCreate (HCONNECTION conn, NDFILEINFOL *pfiparent, NDFILEHANDLE *phandle, char *szFileName, ULONG ulSize, ULONG ulOpenMode, ULONG ulAttribute, FEALIST *pFEAList) { return smbopen((Connection *)conn, szFileName, O_CREAT | O_EXCL, ulOpenMode, ulAttribute, pFEAList); } int APIENTRY NdpOpenCreateL(HCONNECTION conn, NDFILEINFO *pfiparent, NDFILEHANDLE *phandle, char *szFileName, LONGLONG llSize, ULONG ulOpenMode, ULONG ulAttribute, FEALIST *pFEAList) { return smbopen((Connection *)conn, szFileName, O_CREAT | O_EXCL, ulOpenMode, ulAttribute, pFEAList); } int APIENTRY NdpOpenExisting (HCONNECTION conn, NDFILEINFOL *pfi, NDFILEHANDLE *phandle, char *szFileName, ULONG ulOpenMode, USHORT *pfNeedEA) { if (pfNeedEA) *pfNeedEA = 0; // wtf is this ? return smbopen((Connection *)conn, szFileName, 0, ulOpenMode, 0, NULL); } int APIENTRY NdpSetFileAttribute (HCONNECTION conn, NDFILEINFOL *pfi, char *szFileName, USHORT usAttr) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; smbwrp_fileinfo finfo; char path[CCHMAXPATH+1] = {0}; ENTER(); debuglocal(pRes, 9, "NdpSetFileAttribute in [%p]\n", pConn); do { rc = pathparser(pRes, pConn, szFileName, path); if (rc) break; memset(&finfo, 0, sizeof(finfo)); strncpy(finfo.fname, path, sizeof(finfo.fname) - 1); finfo.attr = usAttr & 0x37; rc = smbwrp_setattr(pRes, pConn->cli, &finfo); } while (0); debuglocal(pRes, 9, "NdpSetFileAttribute <%s> (%s) %04x %d\n", szFileName, path, usAttr, rc); LEAVE(); return rc; } int APIENTRY NdpFlush (HRESOURCE resource) { Resource *pRes = (Resource *)resource; debuglocal(pRes, 9, "NdpFlush %d\n", ERROR_NOT_SUPPORTED); return ERROR_NOT_SUPPORTED; } /* * Called when a new thread will call the plugin. * Allows to do per thread init, like disable signals. */ #define ND_PL_INIT_THREAD 0xFFFF0000 int APIENTRY NdpIOCTL (int type, HRESOURCE resource, char *path, int function, void *in, ULONG insize, PULONG poutlen) { Resource *pRes = (Resource *)resource; if (function == ND_PL_INIT_THREAD) { smbwrp_initthread(); debuglocal(pRes, 9, "NdpIOCTL init thread\n"); return NO_ERROR; } debuglocal(pRes, 9, "NdpIOCTL <%s> %d\n", path, function); if (in && insize > 4096) { char out[4096]; sprintf (out, "SAMBA IOCTL function = %d, parms [%s] insize = %d, *poutlen = %d", function, in, insize, *poutlen); *poutlen = strlen(out); strcpy (in, out); return NO_ERROR; } return ERROR_NOT_SUPPORTED; } int APIENTRY NdpFileQueryInfo (HCONNECTION conn, NDFILEHANDLE handle, void *plist) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; smbwrp_fileinfo finfo; ENTER(); debuglocal(pRes, 9, "NdpFileQueryInfo in [%p]\n", pConn); do { if (pConn->file.fd < 0 || !*pConn->file.fname) { rc = ERROR_INVALID_HANDLE; break; } strncpy(finfo.fname, pConn->file.fname, sizeof(finfo.fname) - 1); rc = smbwrp_fgetattr(pRes, pConn->cli, &pConn->file, &finfo); if (!rc) { finfo.easize = -1; addFindInfoL(pRes, plist, &finfo, 0, NULL); } } while (0); debuglocal(pRes, 9, "NdpFileQueryInfo <%s> %d\n", pConn->file.fd < 0 ? "!null!" : pConn->file.fname, rc); LEAVE(); return rc; } int APIENTRY NdpFileEAQuery (HCONNECTION conn, NDFILEHANDLE handle, GEALIST *pGEAList, FEALIST *pFEAList) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; const int cbBuffer = 64*1024; FEALIST * pFEASrc; if (!pFEAList) return ERROR_EAS_NOT_SUPPORTED; if (!pRes->easupport) return ERROR_EAS_NOT_SUPPORTED; debuglocal(pRes, 9, "NdpFileEAQuery in [%p] <%s>/%d pGEAList=%08x\n", pConn, pConn->file.fname, pConn->file.fd, pGEAList); char *pchBuffer = (char *)malloc(cbBuffer); if (!pchBuffer) return ERROR_NOT_ENOUGH_MEMORY; ENTER(); do { if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } rc = smbwrp_flistea(pRes, pConn->cli, &pConn->file, pchBuffer, cbBuffer); pFEASrc = (FEALIST *)pchBuffer; if (rc) { switch (rc) { case ERROR_FILE_NOT_FOUND : case ERROR_PATH_NOT_FOUND : pFEAList->cbList = sizeof(pFEAList->cbList); rc = NO_ERROR; break; case ERROR_BUFFER_OVERFLOW : pFEAList->cbList = pFEASrc->cbList; break; default : rc = ERROR_EAS_NOT_SUPPORTED; } } else rc = buildFEALIST(pRes, pFEASrc, pGEAList, pFEAList); } while (0); free(pchBuffer); debuglocal(pRes, 9, "NdpFileEAQuery out <%s>/%d pFEASrc->cbList=%d pFEAList->cbList=%d rc=%d\n", pConn->file.fname, pConn->file.fd, pFEASrc->cbList, pFEAList->cbList, rc); LEAVE(); return rc; } int APIENTRY NdpFileEASet (HCONNECTION conn, NDFILEHANDLE handle, FEALIST *pFEAList) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; debuglocal(pRes, 9, "NdpFileEASet in [%p]\n", pConn); if (!pFEAList || pFEAList->cbList <= sizeof(long)) return ERROR_EAS_NOT_SUPPORTED; if (!pRes->easupport) return ERROR_EAS_NOT_SUPPORTED; ENTER(); do { // got FEA there FEA * pfea; unsigned long done = sizeof(long); if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } pfea = pFEAList->list; while (done < pFEAList->cbList) { rc = smbwrp_fsetea(pRes, pConn->cli, &pConn->file, (char *)(pfea + 1), pfea->cbValue ? (char *)(pfea + 1) + pfea->cbName + 1: NULL, pfea->cbValue); if (rc) break; pfea = (FEA *)((char *)(pfea + 1) + pfea->cbName + 1 + pfea->cbValue); done += sizeof(FEA) + pfea->cbName + 1 + pfea->cbValue; } } while (0); debuglocal(pRes, 9, "NdpFileEASet %d\n", rc); LEAVE(); return rc; } int APIENTRY NdpFileEASize (HCONNECTION conn, NDFILEHANDLE handle, ULONG *pulEASize) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; char path[CCHMAXPATH+1] = {0}; FEALIST * pFEAList; const int cbBuffer = 64*1024; if (!pulEASize) return ERROR_EAS_NOT_SUPPORTED; if (!pRes->easupport) return ERROR_EAS_NOT_SUPPORTED; debuglocal(pRes, 9, "NdpFileEASize in [%p] <%s>/%d \n", pConn, pConn->file.fname, pConn->file.fd); char *pchBuffer = (char *)malloc(cbBuffer); if (!pchBuffer) return ERROR_NOT_ENOUGH_MEMORY; ENTER(); do { if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } rc = smbwrp_flistea(pRes, pConn->cli, &pConn->file, pchBuffer, cbBuffer); pFEAList = (FEALIST*)pchBuffer; if (rc) { switch (rc) { case ERROR_FILE_NOT_FOUND : case ERROR_PATH_NOT_FOUND : pFEAList->cbList = sizeof(pFEAList->cbList);// Fall through */ case ERROR_BUFFER_OVERFLOW : rc = NO_ERROR; break; default : rc = ERROR_EAS_NOT_SUPPORTED; } } *pulEASize = pFEAList->cbList; } while (0); free(pchBuffer); debuglocal(pRes, 9, "NdpFileEASize %d %d\n", *pulEASize, rc); LEAVE(); return rc; } int APIENTRY NdpFileSetInfo (HCONNECTION conn, NDFILEHANDLE handle, NDFILEINFOL *pfi) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action, attrFile; ENTER(); debuglocal(pRes, 9, "NdpFileSetInfo in [%p]\n", pConn); // delete the dir cache dircache_invalidate(pRes->pdc, pConn->file.fullname, 1); do { if (pConn->file.fd < 0 || !*pConn->file.fname) { rc = ERROR_INVALID_HANDLE; break; } attrFile = pfi->stat.attrFile; // deferred setinfo - on closing the file pConn->file.openattr = attrFile; fsphDosDateToUnixTime(pRes, pfi->stat.fdateLastWrite, pfi->stat.ftimeLastWrite, &(pConn->file.mtime)); fsphDosDateToUnixTime(pRes, pfi->stat.fdateCreation, pfi->stat.ftimeCreation, &(pConn->file.ctime)); pConn->file.updatetime = 2; debuglocal(pRes, 9, "NdpFileSetInfo mtime %d\n", pConn->file.mtime); } while (0); debuglocal(pRes, 9, "NdpFileSetInfo <%s> %08x %d\n", pConn->file.fd < 0 ? "!null!" : pConn->file.fname, attrFile, rc); LEAVE(); return NO_ERROR; } int APIENTRY NdpFileSetFilePtrL(HCONNECTION conn, NDFILEHANDLE handle, LONGLONG llOffset, ULONG ulMethod, LONGLONG *pllActual) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; ENTER(); debuglocal(pRes, 9, "NdpFileSetFilePtrL in [%p]\n", pConn); do { if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } rc = smbwrp_lseek(pRes, pConn->cli, &pConn->file, ulMethod, llOffset); if (!rc) *pllActual = pConn->file.offset; } while (0); debuglocal(pRes, 9, "NdpFileSetFilePtrL <%s> %lld %lu %lld %d\n", pConn->file.fd < 0 ? "!null!" : pConn->file.fname, llOffset, ulMethod, *pllActual, rc); LEAVE(); return rc; } int APIENTRY NdpFileSetFilePtr (HCONNECTION conn, NDFILEHANDLE handle, LONG lOffset, ULONG ulMethod, ULONG *pulActual) { Connection *pConn = (Connection *)conn; LONGLONG llActual; int rc = NdpFileSetFilePtrL(conn, handle, lOffset, ulMethod, &llActual); *pulActual = llActual & 0xFFFFFFFF; debuglocal(pConn->pRes, 9, "NdpFileSetFilePtr %ld %lu %ld %d\n", lOffset, ulMethod, *pulActual, rc); return rc; } int APIENTRY NdpFileClose (HCONNECTION conn, NDFILEHANDLE handle) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; ENTER(); debuglocal(pRes, 9, "NdpFileClose in [%p] %d <%s>\n", pConn, pConn->file.fd, pConn->file.fd < 0 ? "!null!" : pConn->file.fname); do { if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } rc = smbwrp_close(pRes, pConn->cli, &pConn->file); } while (0); debuglocal(pRes, 9, "NdpFileClose %d %d\n", pConn->file.fd, rc); pConn->file.fd = -1; LEAVE(); return rc; } int APIENTRY NdpFileCommit (HCONNECTION conn, NDFILEHANDLE handle) { Connection *pConn = (Connection *)conn; debuglocal(pConn->pRes, 9, "NdpFileCommit %d\n", NO_ERROR); return NO_ERROR; } int APIENTRY NdpFileNewSize (HCONNECTION conn, NDFILEHANDLE handle, ULONG ulLen) { Connection *pConn = (Connection *)conn; int rc = NdpFileNewSizeL(conn, handle, ulLen); debuglocal(pConn->pRes, 9, "NdpFileNewSize %ld %d\n", ulLen, rc); return rc; } int APIENTRY NdpFileNewSizeL(HCONNECTION conn, NDFILEHANDLE handle, LONGLONG llLen) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long action; ENTER(); debuglocal(pRes, 9, "NdpFileNewSizeL in [%p]\n", pConn); do { if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } rc = smbwrp_setfilesize(pRes, pConn->cli, &pConn->file, llLen); } while (0); debuglocal(pRes, 9, "NdpFileNewSizeL <%s> %lld %d\n", pConn->file.fd < 0 ? "!null!" : pConn->file.fname, llLen, rc); LEAVE(); return rc; } #define NDPSMB_READ_MAX_SIZE (65536 - 4096) int APIENTRY NdpFileRead (HCONNECTION conn, NDFILEHANDLE handle, void *pBuffer, ULONG ulRead, ULONG *pulActual) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long done = 0; unsigned long onedone; unsigned long action; ULONG ulReadCompleted = 0; ENTER(); debuglocal(pRes, 9, "NdpFileRead in [%p]\n", pConn); do { if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } while (ulReadCompleted < ulRead) { ULONG ulActual; ULONG ulToRead = ulRead - ulReadCompleted; debuglocal(pRes, 9, "NdpFileRead completed %d, to read %d\n", ulReadCompleted, ulToRead); if (ulToRead > NDPSMB_READ_MAX_SIZE) ulToRead = NDPSMB_READ_MAX_SIZE; rc = smbwrp_read(pRes, pConn->cli, &pConn->file, (char *)pBuffer + ulReadCompleted, ulToRead, &ulActual); if (ulActual == 0 || rc != NO_ERROR) break; ulReadCompleted += ulActual; } } while (0); if (ulReadCompleted > 0) rc = NO_ERROR; // Still were able to read some data. if (rc == NO_ERROR) *pulActual = ulReadCompleted; debuglocal(pRes, 9, "NdpFileRead <%s> %lu %lu %d\n", pConn->file.fd < 0 ? "!null!" : pConn->file.fname, ulRead, *pulActual, rc); LEAVE(); return rc; } int APIENTRY NdpFileWrite (HCONNECTION conn, NDFILEHANDLE handle, void *pBuffer, ULONG ulWrite, ULONG *pulActual) { Connection *pConn = (Connection *)conn; Resource *pRes = pConn->pRes; int rc = 0; unsigned long done = 0; unsigned long onedone; unsigned long action; ENTER(); debuglocal(pRes, 9, "NdpFileWrite in [%p]\n", pConn); /* * delete the dir cache * This was moved from NdpFileClose() becasue if there are a lot files * in the tree, all are reread. The problem when moved to here is, that * the last accessed time is not refreshed. If this is needed, * a new function needs to be done to update only one file in the cache */ dircache_invalidate(pRes->pdc, pConn->file.fullname, 1); do { if (pConn->file.fd < 0) { rc = ERROR_INVALID_HANDLE; break; } rc = smbwrp_write(pRes, pConn->cli, &pConn->file, pBuffer, ulWrite, pulActual); } while (0); debuglocal(pRes, 9, "NdpFileWrite <%s> %lu %lu %d\n", pConn->file.fd < 0 ? "!null!" : pConn->file.fname, ulWrite, *pulActual, rc); LEAVE(); return rc; }