Beispiel #1
0
bool GfxProc::savefa(string *localfilepath, GfxProc::meta_t type, string *localdstpath)
{
    if (!isgfx(localfilepath)
            // (this assumes that the width of the largest dimension is max)
            || !readbitmap(NULL, localfilepath, dimensions[sizeof dimensions/sizeof dimensions[0]-1][0]))
    {
        return false;
    }

    string jpeg;
    bool success = resizebitmap(dimensions[type][0], dimensions[type][1], &jpeg);
    freebitmap();

    if (!success)
    {
        return false;
    }

    FileAccess *f = client->fsaccess->newfileaccess();
    client->fsaccess->unlinklocal(localdstpath);
    if (!f->fopen(localdstpath, false, true))
    {
        delete f;
        return false;
    }

    if (!f->fwrite((const byte*)jpeg.data(), jpeg.size(), 0))
    {
        delete f;
        return false;
    }

    delete f;
    return true;
}
Beispiel #2
0
bool GfxProc::savefa(string *localfilepath, int width, int height, string *localdstpath)
{
    if (!isgfx(localfilepath))
    {
        return false;
    }

    mutex.lock();
    if (!readbitmap(NULL, localfilepath, width > height ? width : height))
    {
        mutex.unlock();
        return false;
    }

    int w = width;
    int h = height;
    if (this->w < w && this->h < h)
    {
        LOG_debug << "Skipping upsizing of local preview";
        w = this->w;
        h = this->h;
    }

    string jpeg;
    bool success = resizebitmap(w, h, &jpeg);
    freebitmap();
    mutex.unlock();

    if (!success)
    {
        return false;
    }

    FileAccess *f = client->fsaccess->newfileaccess();
    client->fsaccess->unlinklocal(localdstpath);
    if (!f->fopen(localdstpath, false, true))
    {
        delete f;
        return false;
    }

    if (!f->fwrite((const byte*)jpeg.data(), unsigned(jpeg.size()), 0))
    {
        delete f;
        return false;
    }

    delete f;
    return true;
}
Beispiel #3
0
// check local path - if !localname, localpath is relative to l, with l == NULL
// being the root of the sync
// if localname is set, localpath is absolute and localname its last component
// path references a new FOLDERNODE: returns created node
// path references a existing FILENODE: returns node
// otherwise, returns NULL
LocalNode* Sync::checkpath(LocalNode* l, string* localpath, string* localname)
{
    LocalNode* ll = l;
    FileAccess* fa;
    bool newnode = false, changed = false;
    bool isroot;

    LocalNode* parent;
    string path;        // UTF-8 representation of tmppath
    string tmppath;     // full path represented by l + localpath
    string newname;     // portion of tmppath not covered by the existing
                        // LocalNode structure (always the last path component
                        // that does not have a corresponding LocalNode yet)

    if (localname)
    {
        // shortcut case (from within syncdown())
        isroot = false;
        parent = l;
        l = NULL;

        client->fsaccess->local2path(localpath, &path);
    }
    else
    {
        // construct full filesystem path in tmppath
        if (l)
        {
            l->getlocalpath(&tmppath);
        }

        if (localpath->size())
        {
            if (tmppath.size())
            {
                tmppath.append(client->fsaccess->localseparator);
            }

            tmppath.append(*localpath);
        }

        // look up deepest existing LocalNode by path, store remainder (if any)
        // in newname
        l = localnodebypath(l, localpath, &parent, &newname);

        // path invalid?
        if (!l && !newname.size())
        {
            return NULL;
        }

        string name = newname;
        client->fsaccess->local2name(&name);

        if (!client->app->sync_syncable(name.c_str(), &tmppath, &newname))
        {
            return NULL;
        }

        isroot = (l == &localroot && !newname.size());

        client->fsaccess->local2path(&tmppath, &path);
    }

    // postpone moving nodes into nonexistent parents
    if (parent && !parent->node)
    {
        return (LocalNode*)~0;
    }

    // attempt to open/type this file
    fa = client->fsaccess->newfileaccess();

    if (fa->fopen(localname ? localpath : &tmppath, true, false))
    {
        // match cached LocalNode state during initial/rescan to prevent costly re-fingerprinting
        // (just compare the fsids, sizes and mtimes to detect changes)
        if (fullscan)
        {
            // find corresponding LocalNode by file-/foldername
            int lastpart = client->fsaccess->lastpartlocal(localname ? localpath : &tmppath);

            string fname(localname ? *localpath : tmppath,
                                   lastpart,
                                   (localname ? *localpath : tmppath).size()-lastpart);

            LocalNode* cl = (parent ? parent : &localroot)->childbyname(&fname);

            if (cl && fa->fsid == cl->fsid)
            {
                // node found and same file
                l = cl;
                l->deleted = false;
                l->setnotseen(0);

                // if it's a file, size and mtime must match to qualify
                if (l->type != FILENODE || (l->size == fa->size && l->mtime == fa->mtime))
                {
                    l->scanseqno = scanseqno;

                    if (l->type == FOLDERNODE)
                    {
                        scan(localname ? localpath : &tmppath, fa);
                    }
                    else localbytes += l->size;

                    delete fa;
                    return l;
                }
            }
        }

        if (!isroot)
        {
            if (l)
            {
                // mark as present
                l->setnotseen(0);

                if (fa->type == FILENODE)
                {
                    // has the file been overwritten or changed since the last scan?
                    // or did the size or mtime change?
                    if (fa->fsidvalid)
                    {
                        // if fsid has changed, the file was overwritten
                        // (FIXME: handle type changes)
                        if (l->fsid != fa->fsid)
                        {
                            handlelocalnode_map::iterator it;

                            // was the file overwritten by moving an existing
                            // file over it?
                            if ((it = client->fsidnode.find(fa->fsid)) != client->fsidnode.end())
                            {
                                client->app->syncupdate_local_move(this, it->second->name.c_str(), path.c_str());

                                // immediately delete existing LocalNode and
                                // replace with moved one
                                delete l;

                                // (in case of a move, this synchronously
                                // updates l->parent and l->node->parent)
                                it->second->setnameparent(parent, localname ? localpath : &tmppath);

                                // mark as seen / undo possible deletion
                                it->second->setnotseen(0);

                                statecacheadd(it->second);

                                delete fa;
                                return it->second;
                            }
                            else
                            {
                                l->mtime = -1;  // trigger change detection
                            }
                        }
                    }

                    // no fsid change detected or overwrite with unknown file:
                    if (fa->mtime != l->mtime || fa->size != l->size)
                    {
                        if (fa->fsidvalid && (l->fsid != fa->fsid))
                        {
                            l->setfsid(fa->fsid);
                        }

                        m_off_t dsize = l->size;

                        if (l->genfingerprint(fa))
                        {
                            localbytes -= dsize - l->size;
                        }

                        client->app->syncupdate_local_file_change(this, path.c_str());

                        client->stopxfer(l);
                        l->bumpnagleds();
                        l->deleted = false;

                        client->syncactivity = true;

                        statecacheadd(l);

                        delete fa;
                        return l;
                    }
                }
                else
                {
                    // (we tolerate overwritten folders, because we do a
                    // content scan anyway)
                    if (fa->fsidvalid)
                    {
                        l->setfsid(fa->fsid);
                    }
                }
            }

            // new node
            if (!l)
            {
                // rename or move of existing node?
                handlelocalnode_map::iterator it;

                if (fa->fsidvalid && ((it = client->fsidnode.find(fa->fsid)) != client->fsidnode.end()))
                {
                    client->app->syncupdate_local_move(this, it->second->name.c_str(), path.c_str());

                    // (in case of a move, this synchronously updates l->parent
                    // and l->node->parent)
                    it->second->setnameparent(parent, localname ? localpath : &tmppath);

                    // make sure that active PUTs receive their updated filenames
                    client->updateputs();

                    statecacheadd(it->second);

                    // unmark possible deletion
                    it->second->setnotseen(0);

                    // immediately scan folder to detect deviations from cached state
                    if (fullscan)
					{
                        scan(localname ? localpath : &tmppath, fa);
                    }
                }
                else
                {
                    // this is a new node: add
                    l = new LocalNode;
                    l->init(this, fa->type, parent, localname ? localpath : &tmppath);

                    if (fa->fsidvalid)
                    {
                        l->setfsid(fa->fsid);
                    }

                    newnode = true;
                }
            }
        }

        if (l)
        {
            // detect file changes or recurse into new subfolders
            if (l->type == FOLDERNODE)
            {
                if (newnode)
                {
                    scan(localname ? localpath : &tmppath, fa);
                    client->app->syncupdate_local_folder_addition(this, path.c_str());

                    if (!isroot)
                    {
                        statecacheadd(l);
                    }
                }
                else
                {
                    l = NULL;
                }
            }
            else
            {
                if (isroot)
                {
                    // root node cannot be a file
                    changestate(SYNC_FAILED);
                }
                else 
                {
                    if (l->size > 0)
                    {
                        localbytes -= l->size;
                    }

                    if (l->genfingerprint(fa))
                    {
                        changed = true;
                        l->bumpnagleds();
                        l->deleted = false;
                    }

                    if (l->size > 0)
                    {
                        localbytes += l->size;
                    }

                    if (newnode)
                    {
                        client->app->syncupdate_local_file_addition(this, path.c_str());
                    }
                    else if (changed)
                    {
                        client->app->syncupdate_local_file_change(this, path.c_str());
                    }

                    if (newnode || changed)
                    {
                        statecacheadd(l);
                    }
                }
            }
        }

        if (changed || newnode)
        {
            client->syncactivity = true;
        }
    }
    else
    {
        if (fa->retry)
        {
            // fopen() signals that the failure is potentially transient - do
            // nothing and request a recheck
            dirnotify->notify(DirNotify::RETRY, ll, localpath->data(), localpath->size());
        }
        else if (l)
        {
            // immediately stop outgoing transfer, if any
            if (l->transfer)
            {
                client->stopxfer(l);
            }

            client->syncactivity = true;

            // in fullscan mode, missing files are handled in bulk in deletemissing()
            // rather than through setnotseen()
            if (!fullscan) l->setnotseen(1);
        }

        l = NULL;
    }

    delete fa;

    return l;
}
Beispiel #4
0
// new Syncs are automatically inserted into the session's syncs list
// and a full read of the subtree is initiated
Sync::Sync(MegaClient* cclient, string* crootpath, const char* cdebris,
           string* clocaldebris, Node* remotenode, int ctag)
{
    string dbname;

    client = cclient;
    tag = ctag;

    tmpfa = NULL;

    localbytes = 0;
    localnodes[FILENODE] = 0;
    localnodes[FOLDERNODE] = 0;

    state = SYNC_INITIALSCAN;

    fullscan = true;
	
    if (cdebris)
    {
        debris = cdebris;
        client->fsaccess->path2local(&debris, &localdebris);

        dirnotify = client->fsaccess->newdirnotify(crootpath, &localdebris);

        localdebris.insert(0, client->fsaccess->localseparator);
        localdebris.insert(0, *crootpath);
    }
    else
    {
        localdebris = *clocaldebris;

        // FIXME: pass last segment of localdebris
        dirnotify = client->fsaccess->newdirnotify(crootpath, &localdebris);
    }

    localroot.init(this, FOLDERNODE, NULL, crootpath);
    localroot.setnode(remotenode);

    sync_it = client->syncs.insert(client->syncs.end(), this);

    if (client->dbaccess)
    {
        // open state cache table
        handle tableid[3];
        string dbname;

        FileAccess *fas = client->fsaccess->newfileaccess();

        if (fas->fopen(crootpath, true, false))
        {
            tableid[0] = fas->fsid;
            tableid[1] = remotenode->nodehandle;
            tableid[2] = client->me;

            dbname.resize(sizeof tableid * 4 / 3 + 3);
            dbname.resize(Base64::btoa((byte*)tableid, sizeof tableid, (char*)dbname.c_str()));

            statecachetable = client->dbaccess->open(client->fsaccess, &dbname);

            readstatecache();
        }

        delete fas;
    }
}
Beispiel #5
0
// transfer completion: copy received file locally, set timestamp(s), verify
// fingerprint, notify app, notify files
void Transfer::complete()
{
    if (type == GET)
    {
        LOG_debug << "Download complete: " << (files.size() ? LOG_NODEHANDLE(files.front()->h) : "NO_FILES") << " " << files.size();

        bool transient_error = false;
        string tmplocalname;
        string localname;
        bool success;

        // disconnect temp file from slot...
        delete slot->fa;
        slot->fa = NULL;

        // FIXME: multiple overwrite race conditions below (make copies
        // from open file instead of closing/reopening!)

        // set timestamp (subsequent moves & copies are assumed not to alter mtime)
        success = client->fsaccess->setmtimelocal(&localfilename, mtime);

#ifdef ENABLE_SYNC
        if (!success)
        {
            transient_error = client->fsaccess->transient_error;
            LOG_debug << "setmtimelocal failed " << transient_error;
        }
#endif

        // verify integrity of file
        FileAccess* fa = client->fsaccess->newfileaccess();
        FileFingerprint fingerprint;
        Node* n;
        bool fixfingerprint = false;

        if (!transient_error && fa->fopen(&localfilename, true, false))
        {
            fingerprint.genfingerprint(fa);

            if (isvalid && !(fingerprint == *(FileFingerprint*)this))
            {
                if (!badfp.isvalid || !(badfp == fingerprint))
                {
                    badfp = fingerprint;
                    delete fa;
                    client->fsaccess->unlinklocal(&localfilename);
                    return failed(API_EWRITE);
                }
                else
                {
                    fixfingerprint = true;
                }
            }
        }
#ifdef ENABLE_SYNC
        else
        {
            if (!transient_error)
            {
                transient_error = fa->retry;
                LOG_debug << "Unable to validate fingerprint " << transient_error;
            }
        }
#endif
        delete fa;

        int missingattr = 0;
        handle attachh;
        SymmCipher* symmcipher;

        if (!transient_error)
        {
            // set FileFingerprint on source node(s) if missing
            for (file_list::iterator it = files.begin(); it != files.end(); it++)
            {
                if ((*it)->hprivate && (n = client->nodebyhandle((*it)->h)))
                {
                    if (client->gfx && client->gfx->isgfx(&(*it)->localname))
                    {
                        // check for missing imagery
                        if (!n->hasfileattribute(GfxProc::THUMBNAIL120X120)) missingattr |= 1 << GfxProc::THUMBNAIL120X120;
                        if (!n->hasfileattribute(GfxProc::PREVIEW1000x1000)) missingattr |= 1 << GfxProc::PREVIEW1000x1000;
                        attachh = n->nodehandle;
                        symmcipher = n->nodecipher();
                    }

                    if (fingerprint.isvalid && (!n->isvalid || fixfingerprint))
                    {
                        *(FileFingerprint*)n = fingerprint;

                        n->serializefingerprint(&n->attrs.map['c']);
                        client->setattr(n);
                    }
                }
            }

            if (fingerprint.isvalid && fixfingerprint)
            {
                (*(FileFingerprint*)this) = fingerprint;
            }

            if (missingattr)
            {
                // FIXME: do this while file is still open
                client->gfx->gendimensionsputfa(NULL, &localfilename, attachh, symmcipher, missingattr);
            }

            // ...and place it in all target locations. first, update the files'
            // local target filenames, in case they have changed during the upload
            for (file_list::iterator it = files.begin(); it != files.end(); it++)
            {
                (*it)->updatelocalname();
            }

            // place file in all target locations - use up to one renames, copy
            // operations for the rest
            // remove and complete successfully completed files
            for (file_list::iterator it = files.begin(); it != files.end(); )
            {
                transient_error = false;
                success = false;
                localname = (*it)->localname;

                fa = client->fsaccess->newfileaccess();
                if (fa->fopen(&localname))
                {
                    // the destination path already exists
    #ifdef ENABLE_SYNC
                    if((*it)->syncxfer)
                    {
                        sync_list::iterator it2;
                        for (it2 = client->syncs.begin(); it2 != client->syncs.end(); it2++)
                        {
                            Sync *sync = (*it2);
                            LocalNode *localNode = sync->localnodebypath(NULL, &localname);
                            if (localNode)
                            {
                                LOG_debug << "Overwritting a local synced file. Moving the previous one to debris";

                                // try to move to local debris
                                if(!sync->movetolocaldebris(&localname))
                                {
                                    transient_error = client->fsaccess->transient_error;
                                }

                                break;
                            }
                        }

                        if(it2 == client->syncs.end())
                        {
                            LOG_err << "LocalNode for destination file not found";

                            if(client->syncs.size())
                            {
                                // try to move to debris in the first sync
                                if(!client->syncs.front()->movetolocaldebris(&localname))
                                {
                                    transient_error = client->fsaccess->transient_error;
                                }
                            }
                        }
                    }
                    else
    #endif
                    {
                        LOG_debug << "The destination file exist (not synced). Saving with a different name";

                        // the destination path isn't synced, save with a (x) suffix
                        string utf8fullname;
                        client->fsaccess->local2path(&localname, &utf8fullname);
                        size_t dotindex = utf8fullname.find_last_of('.');
                        string name;
                        string extension;
                        if (dotindex == string::npos)
                        {
                            name = utf8fullname;
                        }
                        else
                        {
                            string separator;
                            client->fsaccess->local2path(&client->fsaccess->localseparator, &separator);
                            size_t sepindex = utf8fullname.find_last_of(separator);
                            if(sepindex == string::npos || sepindex < dotindex)
                            {
                                name = utf8fullname.substr(0, dotindex);
                                extension = utf8fullname.substr(dotindex);
                            }
                            else
                            {
                                name = utf8fullname;
                            }
                        }

                        string suffix;
                        string newname;
                        string localnewname;
                        int num = 0;
                        do
                        {
                            num++;
                            ostringstream oss;
                            oss << " (" << num << ")";
                            suffix = oss.str();
                            newname = name + suffix + extension;
                            client->fsaccess->path2local(&newname, &localnewname);
                        } while (fa->fopen(&localnewname));


                        (*it)->localname = localnewname;
                        localname = localnewname;
                    }
                }
                else
                {
                    transient_error = fa->retry;
                }

                delete fa;
                if (transient_error)
                {
                    LOG_warn << "Transient error checking if the destination file exist";
                    it++;
                    continue;
                }

                if (!tmplocalname.size())
                {
                    if (client->fsaccess->renamelocal(&localfilename, &localname))
                    {
                        tmplocalname = localname;
                        success = true;
                    }
                    else if (client->fsaccess->transient_error)
                    {
                        transient_error = true;
                    }
                }

                if (!success)
                {
                    if((tmplocalname.size() ? tmplocalname : localfilename) == localname)
                    {
                        LOG_debug << "Identical node downloaded to the same folder";
                        success = true;
                    }
                    else if (client->fsaccess->copylocal(tmplocalname.size() ? &tmplocalname : &localfilename,
                                                   &localname, mtime))
                    {
                        success = true;
                    }
                    else if (client->fsaccess->transient_error)
                    {
                        transient_error = true;
                    }
                }

                if (success || !transient_error)
                {
                    if (success)
                    {
                        // prevent deletion of associated Transfer object in completed()
                        (*it)->transfer = NULL;
                        (*it)->completed(this, NULL);
                    }

                    if (success || !(*it)->failed(API_EAGAIN))
                    {
                        File* f = (*it);
                        files.erase(it++);
                        if(!success)
                        {
                            LOG_warn << "Unable to complete transfer due to a persistent error";

                            f->transfer = NULL;
                            f->terminated();
                        }
                    }
                    else
                    {
                        LOG_debug << "Persistent error completing file";
                        it++;
                    }
                }
                else
                {
                    LOG_debug << "Transient error completing file";
                    it++;
                }
            }

            if (!tmplocalname.size() && !files.size())
            {
                client->fsaccess->unlinklocal(&localfilename);
            }
        }

        if (!files.size())
        {
            localfilename = localname;
            client->app->transfer_complete(this);
            delete this;
        }
        else
        {
            // some files are still pending completion, close fa and set retry timer
            delete slot->fa;
            slot->fa = NULL;

            LOG_debug << "Files pending completion: " << files.size() << ". Waiting for a retry.";
            LOG_debug << "First pending file: " << files.front()->name;

            slot->retrying = true;
            slot->retrybt.backoff(11);
        }
    }
    else
    {
        LOG_debug << "Upload complete: " << (files.size() ? files.front()->name : "NO_FILES") << " " << files.size();

        // files must not change during a PUT transfer
        if (genfingerprint(slot->fa, true))
        {
            return failed(API_EREAD);
        }

        // if this transfer is put on hold, do not complete
        client->checkfacompletion(uploadhandle, this);
        return;
    }
}