12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675 |
- /****************************************************************************
- * fs/unionfs/fs_unionfs.c
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership. The
- * ASF licenses this file to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the
- * License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <sys/types.h>
- #include <sys/statfs.h>
- #include <sys/stat.h>
- #include <sys/mount.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <string.h>
- #include <fcntl.h>
- #include <assert.h>
- #include <errno.h>
- #include <fixedmath.h>
- #include <debug.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/fs/unionfs.h>
- #include <nuttx/fs/dirent.h>
- #include <nuttx/fs/ioctl.h>
- #include <nuttx/semaphore.h>
- #include "inode/inode.h"
- #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_UNIONFS)
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- #undef MIN
- #undef MAX
- #define MIN(a,b) (((a) < (b)) ? (a) : (b))
- #define MAX(a,b) (((a) > (b)) ? (a) : (b))
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /* This structure describes one contained file system mountpoint */
- struct unionfs_mountpt_s
- {
- FAR struct inode *um_node; /* Filesystem inode */
- FAR char *um_prefix; /* Path prefix to filesystem */
- };
- /* This structure describes the union file system */
- struct unionfs_inode_s
- {
- struct unionfs_mountpt_s ui_fs[2]; /* Contained file systems */
- sem_t ui_exclsem; /* Enforces mutually exclusive access */
- int16_t ui_nopen; /* Number of open references */
- bool ui_unmounted; /* File system has been unmounted */
- };
- /* This structure descries one opened file */
- struct unionfs_file_s
- {
- uint8_t uf_ndx; /* Filesystem index */
- FAR struct file uf_file; /* Filesystem open file description */
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- /* Helper functions */
- static int unionfs_semtake(FAR struct unionfs_inode_s *ui, bool noint);
- #define unionfs_semgive(ui) (void)nxsem_post(&(ui)->ui_exclsem)
- static FAR const char *unionfs_offsetpath(FAR const char *relpath,
- FAR const char *prefix);
- static bool unionfs_ispartprefix(FAR const char *partprefix,
- FAR const char *prefix);
- static int unionfs_tryopen(FAR struct file *filep,
- FAR const char *relpath, FAR const char *prefix, int oflags,
- mode_t mode);
- static int unionfs_tryopendir(FAR struct inode *inode,
- FAR const char *relpath, FAR const char *prefix,
- FAR struct fs_dirent_s *dir);
- static int unionfs_trymkdir(FAR struct inode *inode,
- FAR const char *relpath, FAR const char *prefix,
- mode_t mode);
- static int unionfs_tryrmdir(FAR struct inode *inode,
- FAR const char *relpath, FAR const char *prefix);
- static int unionfs_tryunlink(FAR struct inode *inode,
- FAR const char *relpath, FAR const char *prefix);
- static int unionfs_tryrename(FAR struct inode *mountpt,
- FAR const char *oldrelpath, FAR const char *newrelpath,
- FAR const char *prefix);
- static int unionfs_trystat(FAR struct inode *inode,
- FAR const char *relpath, FAR const char *prefix,
- FAR struct stat *buf);
- static int unionfs_trystatdir(FAR struct inode *inode,
- FAR const char *relpath, FAR const char *prefix);
- static int unionfs_trystatfile(FAR struct inode *inode,
- FAR const char *relpath, FAR const char *prefix);
- static FAR char *unionfs_relpath(FAR const char *path,
- FAR const char *name);
- static int unionfs_unbind_child(FAR struct unionfs_mountpt_s *um);
- static void unionfs_destroy(FAR struct unionfs_inode_s *ui);
- /* Operations on opened files (with struct file) */
- static int unionfs_open(FAR struct file *filep, const char *relpath,
- int oflags, mode_t mode);
- static int unionfs_close(FAR struct file *filep);
- static ssize_t unionfs_read(FAR struct file *filep, FAR char *buffer,
- size_t buflen);
- static ssize_t unionfs_write(FAR struct file *filep, FAR const char *buffer,
- size_t buflen);
- static off_t unionfs_seek(FAR struct file *filep, off_t offset,
- int whence);
- static int unionfs_ioctl(FAR struct file *filep, int cmd,
- unsigned long arg);
- static int unionfs_sync(FAR struct file *filep);
- static int unionfs_dup(FAR const struct file *oldp,
- FAR struct file *newp);
- static int unionfs_fstat(FAR const struct file *filep,
- FAR struct stat *buf);
- static int unionfs_truncate(FAR struct file *filep, off_t length);
- /* Operations on directories */
- static int unionfs_opendir(struct inode *mountpt, const char *relpath,
- FAR struct fs_dirent_s *dir);
- static int unionfs_closedir(FAR struct inode *mountpt,
- FAR struct fs_dirent_s *dir);
- static int unionfs_readdir(FAR struct inode *mountpt,
- FAR struct fs_dirent_s *dir);
- static int unionfs_rewinddir(FAR struct inode *mountpt,
- FAR struct fs_dirent_s *dir);
- static int unionfs_bind(FAR struct inode *blkdriver,
- FAR const void *data, FAR void **handle);
- static int unionfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
- unsigned int flags);
- static int unionfs_statfs(FAR struct inode *mountpt,
- FAR struct statfs *buf);
- /* Operations on paths */
- static int unionfs_unlink(FAR struct inode *mountpt,
- FAR const char *relpath);
- static int unionfs_mkdir(FAR struct inode *mountpt,
- FAR const char *relpath, mode_t mode);
- static int unionfs_rmdir(FAR struct inode *mountpt,
- FAR const char *relpath);
- static int unionfs_rename(FAR struct inode *mountpt,
- FAR const char *oldrelpath, FAR const char *newrelpath);
- static int unionfs_stat(FAR struct inode *mountpt,
- FAR const char *relpath, FAR struct stat *buf);
- /* Initialization */
- static int unionfs_getmount(FAR const char *path,
- FAR struct inode **inode);
- static int unionfs_dobind(FAR const char *fspath1,
- FAR const char *prefix1, FAR const char *fspath2,
- FAR const char *prefix2, FAR void **handle);
- /****************************************************************************
- * Public Data
- ****************************************************************************/
- /* See fs_mount.c -- this structure is explicitly extern'ed there.
- * We use the old-fashioned kind of initializers so that this will compile
- * with any compiler.
- */
- const struct mountpt_operations unionfs_operations =
- {
- unionfs_open, /* open */
- unionfs_close, /* close */
- unionfs_read, /* read */
- unionfs_write, /* write */
- unionfs_seek, /* seek */
- unionfs_ioctl, /* ioctl */
- unionfs_sync, /* sync */
- unionfs_dup, /* dup */
- unionfs_fstat, /* fstat */
- unionfs_truncate, /* truncate */
- unionfs_opendir, /* opendir */
- unionfs_closedir, /* closedir */
- unionfs_readdir, /* readdir */
- unionfs_rewinddir, /* rewinddir */
- unionfs_bind, /* bind */
- unionfs_unbind, /* unbind */
- unionfs_statfs, /* statfs */
- unionfs_unlink, /* unlink */
- unionfs_mkdir, /* mkdir */
- unionfs_rmdir, /* rmdir */
- unionfs_rename, /* rename */
- unionfs_stat /* stat */
- };
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: unionfs_semtake
- ****************************************************************************/
- static int unionfs_semtake(FAR struct unionfs_inode_s *ui, bool noint)
- {
- if (noint)
- {
- return nxsem_wait_uninterruptible(&ui->ui_exclsem);
- }
- else
- {
- return nxsem_wait(&ui->ui_exclsem);
- }
- }
- /****************************************************************************
- * Name: unionfs_offsetpath
- ****************************************************************************/
- static FAR const char *unionfs_offsetpath(FAR const char *relpath,
- FAR const char *prefix)
- {
- FAR const char *trypath;
- int pfxlen;
- /* Is there a prefix on the path to this file system? */
- if (prefix && (pfxlen = strlen(prefix)) > 0)
- {
- /* Does the prefix match? */
- if (strncmp(prefix, relpath, pfxlen) != 0)
- {
- /* No, then this relative cannot be within this file system */
- return NULL;
- }
- /* Skip over the prefix */
- trypath = relpath + pfxlen;
- /* Make sure that what is left is a valid, relative path */
- for (; *trypath == '/'; trypath++);
- }
- else
- {
- /* No.. use the full, relative path */
- trypath = relpath;
- }
- return trypath;
- }
- /****************************************************************************
- * Name: unionfs_ispartprefix
- ****************************************************************************/
- static bool unionfs_ispartprefix(FAR const char *partprefix,
- FAR const char *prefix)
- {
- int partlen = 0;
- int pfxlen = 0;
- /* Trim any '/' characters in the partial prefix */
- if (partprefix != NULL)
- {
- /* Skip over any leading '/' */
- for (; *partprefix == '/'; partprefix++);
- /* Skip over any tailing '/' */
- partlen = strlen(partprefix);
- for (; partlen > 1 && partprefix[partlen - 1] == '/'; partlen--);
- }
- /* Check for NUL or empty partial prefix */
- if (partprefix == NULL || *partprefix == '\0')
- {
- /* A NUL partial prefix is always contained in the full prefix, even
- * if there is no prefix.
- */
- return true;
- }
- /* Trim any '/' characters in the partial prefix */
- if (prefix != NULL)
- {
- /* Skip over any leading '/' */
- for (; *prefix == '/'; prefix++);
- /* Skip over any tailing '/' */
- pfxlen = strlen(prefix);
- for (; pfxlen > 1 && prefix[pfxlen - 1] == '/'; pfxlen--);
- }
- /* Check for NUL or empty full prefix */
- if (prefix == NULL || *prefix == '\0')
- {
- /* A non-NUL partial path cannot be a contained in a NUL prefix */
- return false;
- }
- #if 0 /* Only whole offset is currently supported */
- /* Both the partial path and the prefix are non-NULL. Check if the partial
- * path is contained in the prefix.
- */
- if (partlen > pfxlen)
- {
- return false;
- }
- #else
- /* Check if the trimmed offsets are identical */
- if (partlen != pfxlen)
- {
- return false;
- }
- #endif
- if (strncmp(partprefix, prefix, partlen) == 0)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- /****************************************************************************
- * Name: unionfs_tryopen
- ****************************************************************************/
- static int unionfs_tryopen(FAR struct file *filep, FAR const char *relpath,
- FAR const char *prefix, int oflags, mode_t mode)
- {
- FAR const struct mountpt_operations *ops;
- FAR const char *trypath;
- /* Is this path valid on this file system? */
- trypath = unionfs_offsetpath(relpath, prefix);
- if (trypath == NULL)
- {
- /* No.. return -ENOENT */
- return -ENOENT;
- }
- /* Yes.. try to open this directory */
- DEBUGASSERT(filep->f_inode != NULL && filep->f_inode->u.i_mops != NULL);
- ops = filep->f_inode->u.i_mops;
- if (!ops->open)
- {
- return -ENOSYS;
- }
- return ops->open(filep, trypath, oflags, mode);
- }
- /****************************************************************************
- * Name: unionfs_tryopendir
- ****************************************************************************/
- static int unionfs_tryopendir(FAR struct inode *inode,
- FAR const char *relpath,
- FAR const char *prefix,
- FAR struct fs_dirent_s *dir)
- {
- FAR const struct mountpt_operations *ops;
- FAR const char *trypath;
- /* Is this path valid on this file system? */
- trypath = unionfs_offsetpath(relpath, prefix);
- if (trypath == NULL)
- {
- /* No.. return -ENOENT */
- return -ENOENT;
- }
- /* Yes.. Try to open this directory */
- ops = inode->u.i_mops;
- DEBUGASSERT(ops && ops->opendir);
- if (!ops->opendir)
- {
- return -ENOSYS;
- }
- return ops->opendir(inode, trypath, dir);
- }
- /****************************************************************************
- * Name: unionfs_trymkdir
- ****************************************************************************/
- static int unionfs_trymkdir(FAR struct inode *inode, FAR const char *relpath,
- FAR const char *prefix, mode_t mode)
- {
- FAR const struct mountpt_operations *ops;
- FAR const char *trypath;
- /* Is this path valid on this file system? */
- trypath = unionfs_offsetpath(relpath, prefix);
- if (trypath == NULL)
- {
- /* No.. return -ENOENT */
- return -ENOENT;
- }
- /* Yes.. Try to create the directory */
- ops = inode->u.i_mops;
- if (!ops->mkdir)
- {
- return -ENOSYS;
- }
- return ops->mkdir(inode, trypath, mode);
- }
- /****************************************************************************
- * Name: unionfs_tryrename
- ****************************************************************************/
- static int unionfs_tryrename(FAR struct inode *mountpt,
- FAR const char *oldrelpath,
- FAR const char *newrelpath,
- FAR const char *prefix)
- {
- FAR const struct mountpt_operations *ops;
- FAR const char *tryoldpath;
- FAR const char *trynewpath;
- /* Is source path valid on this file system? */
- tryoldpath = unionfs_offsetpath(oldrelpath, prefix);
- if (tryoldpath == NULL)
- {
- /* No.. return -ENOENT. This should not fail because the existence
- * of the file has already been verified.
- */
- return -ENOENT;
- }
- /* Is source path valid on this file system?
- * REVISIT: There is no logic currently to rename the file by copying i
- * to a different file system. So we just fail if the destination name
- * is not within the same file system. I might, however, be on the other
- * file system and that rename should be supported as a file copy and
- * delete.
- */
- trynewpath = unionfs_offsetpath(newrelpath, prefix);
- if (trynewpath == NULL)
- {
- /* No.. return -ENOSYS. We should be able to do this, but we can't
- * yet.
- */
- return -ENOSYS;
- }
- /* Yes.. Try to rename the file */
- ops = mountpt->u.i_mops;
- if (!ops->rename)
- {
- return -ENOSYS;
- }
- return ops->rename(mountpt, tryoldpath, trynewpath);
- }
- /****************************************************************************
- * Name: unionfs_trystat
- ****************************************************************************/
- static int unionfs_trystat(FAR struct inode *inode, FAR const char *relpath,
- FAR const char *prefix, FAR struct stat *buf)
- {
- FAR const struct mountpt_operations *ops;
- FAR const char *trypath;
- /* Is this path valid on this file system? */
- trypath = unionfs_offsetpath(relpath, prefix);
- if (trypath == NULL)
- {
- /* No.. return -ENOENT */
- return -ENOENT;
- }
- /* Yes.. Try to create the directory */
- ops = inode->u.i_mops;
- if (!ops->stat)
- {
- return -ENOSYS;
- }
- return ops->stat(inode, trypath, buf);
- }
- /****************************************************************************
- * Name: unionfs_trystatdir
- ****************************************************************************/
- static int unionfs_trystatdir(FAR struct inode *inode,
- FAR const char *relpath,
- FAR const char *prefix)
- {
- FAR struct stat buf;
- int ret;
- /* Check if relative path refers to a directory. */
- ret = unionfs_trystat(inode, relpath, prefix, &buf);
- if (ret >= 0 && !S_ISDIR(buf.st_mode))
- {
- return -ENOTDIR;
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_trystatfile
- ****************************************************************************/
- static int unionfs_trystatfile(FAR struct inode *inode,
- FAR const char *relpath,
- FAR const char *prefix)
- {
- FAR struct stat buf;
- int ret;
- /* Check if relative path refers to a regular file. We specifically
- * exclude directories but neither do we expect any kind of special file
- * to reside on the mounted filesystem.
- */
- ret = unionfs_trystat(inode, relpath, prefix, &buf);
- if (ret >= 0 && !S_ISREG(buf.st_mode))
- {
- return -EISDIR;
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_tryrmdir
- ****************************************************************************/
- static int unionfs_tryrmdir(FAR struct inode *inode, FAR const char *relpath,
- FAR const char *prefix)
- {
- FAR const struct mountpt_operations *ops;
- FAR const char *trypath;
- /* Is this path valid on this file system? */
- trypath = unionfs_offsetpath(relpath, prefix);
- if (trypath == NULL)
- {
- /* No.. return -ENOENT */
- return -ENOENT;
- }
- /* Yes.. Try to remove the directory */
- ops = inode->u.i_mops;
- if (!ops->rmdir)
- {
- return -ENOSYS;
- }
- return ops->rmdir(inode, trypath);
- }
- /****************************************************************************
- * Name: unionfs_tryunlink
- ****************************************************************************/
- static int unionfs_tryunlink(FAR struct inode *inode,
- FAR const char *relpath,
- FAR const char *prefix)
- {
- FAR const struct mountpt_operations *ops;
- FAR const char *trypath;
- /* Is this path valid on this file system? */
- trypath = unionfs_offsetpath(relpath, prefix);
- if (trypath == NULL)
- {
- /* No.. return -ENOENT */
- return -ENOENT;
- }
- /* Yes.. Try to unlink the file */
- ops = inode->u.i_mops;
- if (!ops->unlink)
- {
- return -ENOSYS;
- }
- return ops->unlink(inode, trypath);
- }
- /****************************************************************************
- * Name: unionfs_relpath
- ****************************************************************************/
- static FAR char *unionfs_relpath(FAR const char *path, FAR const char *name)
- {
- FAR char *relpath;
- int pathlen;
- int ret;
- /* Check if there is a valid, non-zero-legnth path */
- if (path && (pathlen = strlen(path)) > 0)
- {
- /* Yes.. extend the file name by prepending the path */
- if (path[pathlen - 1] == '/')
- {
- ret = asprintf(&relpath, "%s%s", path, name);
- }
- else
- {
- ret = asprintf(&relpath, "%s/%s", path, name);
- }
- /* Handle errors */
- if (ret < 0)
- {
- return NULL;
- }
- else
- {
- return relpath;
- }
- }
- else
- {
- /* There is no path... just duplicate the name (so that kmm_free()
- * will work later).
- */
- return strdup(name);
- }
- }
- /****************************************************************************
- * Name: unionfs_unbind_child
- ****************************************************************************/
- static int unionfs_unbind_child(FAR struct unionfs_mountpt_s *um)
- {
- FAR struct inode *mpinode = um->um_node;
- FAR struct inode *bdinode = NULL;
- int ret;
- /* Unbind the block driver from the file system (destroying any fs
- * private data). This logic is essentially the same as the logic in
- * nuttx/fs/mount/fs_umount2.c.
- */
- if (!mpinode->u.i_mops->unbind)
- {
- /* The filesystem does not support the unbind operation ??? */
- return -EINVAL;
- }
- /* The unbind method returns the number of references to the file system
- * (i.e., open files), zero if the unbind was performed, or a negated
- * error code on a failure.
- */
- ret = mpinode->u.i_mops->unbind(mpinode->i_private, &bdinode, MNT_FORCE);
- if (ret < 0)
- {
- /* Some failure occurred */
- return ret;
- }
- else if (ret > 0)
- {
- /* REVISIT: This is bad if the file system cannot support a deferred
- * unmount. Ideally it would perform the unmount when the last file
- * is closed. But I don't think any file system do that.
- */
- return -EBUSY;
- }
- /* Successfully unbound */
- mpinode->i_private = NULL;
- /* Release the mountpoint inode and any block driver inode
- * returned by the file system unbind above. This should cause
- * the inode to be deleted (unless there are other references)
- */
- inode_release(mpinode);
- /* Did the unbind method return a contained block driver */
- if (bdinode)
- {
- inode_release(bdinode);
- }
- return OK;
- }
- /****************************************************************************
- * Name: unionfs_destroy
- ****************************************************************************/
- static void unionfs_destroy(FAR struct unionfs_inode_s *ui)
- {
- DEBUGASSERT(ui != NULL && ui->ui_fs[0].um_node != NULL &&
- ui->ui_fs[1].um_node != NULL && ui->ui_nopen == 0);
- /* Unbind the contained file systems */
- unionfs_unbind_child(&ui->ui_fs[0]);
- unionfs_unbind_child(&ui->ui_fs[1]);
- /* Free any allocated prefix strings */
- if (ui->ui_fs[0].um_prefix)
- {
- kmm_free(ui->ui_fs[0].um_prefix);
- }
- if (ui->ui_fs[1].um_prefix)
- {
- kmm_free(ui->ui_fs[1].um_prefix);
- }
- /* And finally free the allocated unionfs state structure as well */
- nxsem_destroy(&ui->ui_exclsem);
- kmm_free(ui);
- }
- /****************************************************************************
- * Name: unionfs_open
- ****************************************************************************/
- static int unionfs_open(FAR struct file *filep, FAR const char *relpath,
- int oflags, mode_t mode)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- int ret;
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- finfo("Opening: ui_nopen=%d\n", ui->ui_nopen);
- /* Get exclusive access to the file system data structures */
- ret = unionfs_semtake(ui, false);
- if (ret < 0)
- {
- return ret;
- }
- /* Allocate a container to hold the open file system information */
- uf = (FAR struct unionfs_file_s *)
- kmm_malloc(sizeof(struct unionfs_file_s));
- if (uf == NULL)
- {
- ret = -ENOMEM;
- goto errout_with_semaphore;
- }
- /* Try to open the file on file system 1 */
- um = &ui->ui_fs[0];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- uf->uf_file.f_oflags = filep->f_oflags;
- uf->uf_file.f_pos = 0;
- uf->uf_file.f_inode = um->um_node;
- uf->uf_file.f_priv = NULL;
- ret = unionfs_tryopen(&uf->uf_file, relpath, um->um_prefix, oflags, mode);
- if (ret >= 0)
- {
- /* Successfully opened on file system 1 */
- uf->uf_ndx = 0;
- }
- else
- {
- /* Try to open the file on file system 1 */
- um = &ui->ui_fs[1];
- uf->uf_file.f_oflags = filep->f_oflags;
- uf->uf_file.f_pos = 0;
- uf->uf_file.f_inode = um->um_node;
- uf->uf_file.f_priv = NULL;
- ret = unionfs_tryopen(&uf->uf_file, relpath, um->um_prefix, oflags,
- mode);
- if (ret < 0)
- {
- goto errout_with_semaphore;
- }
- /* Successfully opened on file system 1 */
- uf->uf_ndx = 1;
- }
- /* Increment the open reference count */
- ui->ui_nopen++;
- DEBUGASSERT(ui->ui_nopen > 0);
- /* Save our private data in the file structure */
- filep->f_priv = (FAR void *)uf;
- ret = OK;
- errout_with_semaphore:
- unionfs_semgive(ui);
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_close
- ****************************************************************************/
- static int unionfs_close(FAR struct file *filep)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- int ret = OK;
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- /* Get exclusive access to the file system data structures */
- ret = unionfs_semtake(ui, false);
- if (ret < 0)
- {
- return ret;
- }
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- finfo("Closing: ui_nopen=%d\n", ui->ui_nopen);
- /* Perform the lower level close operation */
- if (ops->close != NULL)
- {
- ret = ops->close(&uf->uf_file);
- }
- /* Decrement the count of open reference. If that count would go to zero
- * and if the file system has been unmounted, then destroy the file system
- * now.
- */
- if (--ui->ui_nopen <= 0 && ui->ui_unmounted)
- {
- unionfs_destroy(ui);
- }
- /* Free the open file container */
- kmm_free(uf);
- filep->f_priv = NULL;
- unionfs_semgive(ui);
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_read
- ****************************************************************************/
- static ssize_t unionfs_read(FAR struct file *filep, FAR char *buffer,
- size_t buflen)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- finfo("buflen: %lu\n", (unsigned long)buflen);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the lower level read operation */
- return ops->read ? ops->read(&uf->uf_file, buffer, buflen) : -EPERM;
- }
- /****************************************************************************
- * Name: unionfs_write
- ****************************************************************************/
- static ssize_t unionfs_write(FAR struct file *filep, FAR const char *buffer,
- size_t buflen)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- finfo("buflen: %lu\n", (unsigned long)buflen);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the lower level write operation */
- return ops->write ? ops->write(&uf->uf_file, buffer, buflen) : -EPERM;
- }
- /****************************************************************************
- * Name: unionfs_seek
- ****************************************************************************/
- static off_t unionfs_seek(FAR struct file *filep, off_t offset, int whence)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- finfo("offset: %lu whence: %d\n", (unsigned long)offset, whence);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Invoke the file seek method if available */
- if (ops->seek != NULL)
- {
- offset = ops->seek(&uf->uf_file, offset, whence);
- }
- else
- {
- int ret;
- /* Get exclusive access to the file system data structures */
- ret = unionfs_semtake(ui, false);
- if (ret < 0)
- {
- return ret;
- }
- /* No... Just set the common file position value */
- switch (whence)
- {
- case SEEK_CUR:
- offset += filep->f_pos;
- case SEEK_SET:
- if (offset >= 0)
- {
- filep->f_pos = offset; /* Might be beyond the end-of-file */
- }
- else
- {
- offset = (off_t)-EINVAL;
- }
- break;
- case SEEK_END:
- offset = (off_t)-ENOSYS;
- break;
- default:
- offset = (off_t)-EINVAL;
- break;
- }
- unionfs_semgive(ui);
- }
- return offset;
- }
- /****************************************************************************
- * Name: unionfs_ioctl
- ****************************************************************************/
- static int unionfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- finfo("cmd: %d arg: %lu\n", cmd, arg);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the lower level ioctl operation */
- return ops->ioctl ? ops->ioctl(&uf->uf_file, cmd, arg) : -ENOTTY;
- }
- /****************************************************************************
- * Name: unionfs_sync
- ****************************************************************************/
- static int unionfs_sync(FAR struct file *filep)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- finfo("filep=%p\n", filep);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the lower level sync operation */
- return ops->sync ? ops->sync(&uf->uf_file) : -EINVAL;
- }
- /****************************************************************************
- * Name: unionfs_dup
- ****************************************************************************/
- static int unionfs_dup(FAR const struct file *oldp, FAR struct file *newp)
- {
- FAR struct unionfs_file_s *oldpriv;
- FAR struct unionfs_file_s *newpriv;
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- int ret = -ENOMEM;
- finfo("oldp=%p newp=%p\n", oldp, newp);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(oldp != NULL && oldp->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)oldp->f_inode->i_private;
- DEBUGASSERT(ui != NULL && oldp->f_priv != NULL);
- oldpriv = (FAR struct unionfs_file_s *)oldp->f_priv;
- DEBUGASSERT(oldpriv->uf_ndx == 0 || oldpriv->uf_ndx == 1);
- um = &ui->ui_fs[oldpriv->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- DEBUGASSERT(newp != NULL && newp->f_priv == NULL);
- /* Allocate a new container for the union FS open file */
- newpriv = (FAR struct unionfs_file_s *)
- kmm_malloc(sizeof(struct unionfs_file_s));
- if (newpriv != NULL)
- {
- /* Clone the old file structure into the newly allocated one */
- memcpy(newpriv, oldpriv, sizeof(struct unionfs_file_s));
- newpriv->uf_file.f_priv = NULL;
- /* Then perform the lower lowel dup operation */
- ret = OK;
- if (ops->dup != NULL)
- {
- ret = ops->dup(&oldpriv->uf_file, &newpriv->uf_file);
- if (ret < 0)
- {
- kmm_free(newpriv);
- newpriv = NULL;
- }
- }
- /* Save the new container in the new file structure */
- newp->f_priv = newpriv;
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_fstat
- *
- * Description:
- * Obtain information about an open file associated with the file
- * descriptor 'fd', and will write it to the area pointed to by 'buf'.
- *
- ****************************************************************************/
- static int unionfs_fstat(FAR const struct file *filep, FAR struct stat *buf)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- finfo("filep=%p buf=%p\n", filep, buf);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the lower level write operation */
- return ops->fstat ? ops->fstat(&uf->uf_file, buf) : -EPERM;
- }
- /****************************************************************************
- * Name: unionfs_truncate
- *
- * Description:
- * Set the size of the file references by 'filep' to 'length'.
- *
- ****************************************************************************/
- static int unionfs_truncate(FAR struct file *filep, off_t length)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_file_s *uf;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- finfo("filep=%p length=%ld\n", filep, (long)length);
- /* Recover the open file data from the struct file instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- ui = (FAR struct unionfs_inode_s *)filep->f_inode->i_private;
- DEBUGASSERT(ui != NULL && filep->f_priv != NULL);
- uf = (FAR struct unionfs_file_s *)filep->f_priv;
- DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1);
- um = &ui->ui_fs[uf->uf_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the lower level write operation */
- return ops->truncate ? ops->truncate(&uf->uf_file, length) : -EPERM;
- }
- /****************************************************************************
- * Name: unionfs_opendir
- ****************************************************************************/
- static int unionfs_opendir(FAR struct inode *mountpt,
- FAR const char *relpath,
- FAR struct fs_dirent_s *dir)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- FAR struct fs_unionfsdir_s *fu;
- FAR const struct mountpt_operations *ops;
- FAR struct fs_dirent_s *lowerdir;
- int ret;
- finfo("relpath: \"%s\"\n", relpath ? relpath : "NULL");
- /* Recover the filesystem data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- /* Get exclusive access to the file system data structures */
- ret = unionfs_semtake(ui, false);
- if (ret < 0)
- {
- return ret;
- }
- DEBUGASSERT(dir);
- fu = &dir->u.unionfs;
- /* Clone the path. We will need this when we traverse file system 2 to
- * omit duplicates on file system 1.
- */
- if (relpath && strlen(relpath) > 0)
- {
- fu->fu_relpath = strdup(relpath);
- if (!fu->fu_relpath)
- {
- goto errout_with_semaphore;
- }
- }
- /* Allocate another dirent structure for the lower file system */
- lowerdir = (FAR struct fs_dirent_s *)
- kmm_zalloc(sizeof(struct fs_dirent_s));
- if (lowerdir == NULL)
- {
- ret = -ENOMEM;
- goto errout_with_relpath;
- }
- /* Check file system 2 first. */
- um = &ui->ui_fs[1];
- lowerdir->fd_root = um->um_node;
- ret = unionfs_tryopendir(um->um_node, relpath, um->um_prefix, lowerdir);
- if (ret >= 0)
- {
- /* Save the file system 2 access info */
- fu->fu_ndx = 1;
- fu->fu_lower[1] = lowerdir;
- /* Allocate yet another dirent structure for the lower file system 1 */
- lowerdir = (FAR struct fs_dirent_s *)
- kmm_zalloc(sizeof(struct fs_dirent_s));
- if (lowerdir == NULL)
- {
- ret = -ENOMEM;
- goto errout_with_fs2open;
- }
- }
- /* Check if the user is stat'ing some "fake" node between the unionfs root
- * and the file system 1/2 root directory.
- */
- else if (unionfs_ispartprefix(relpath, ui->ui_fs[1].um_prefix))
- {
- /* File system 2 prefix includes this relpath */
- fu->fu_ndx = 1;
- fu->fu_prefix[1] = true;
- }
- /* Check file system 1 last, possibly overwriting fu_ndx */
- um = &ui->ui_fs[0];
- lowerdir->fd_root = um->um_node;
- ret = unionfs_tryopendir(um->um_node, relpath, um->um_prefix, lowerdir);
- if (ret >= 0)
- {
- /* Save the file system 1 access info */
- fu->fu_ndx = 0;
- fu->fu_lower[0] = lowerdir;
- }
- else
- {
- /* File system 1 was not opened... then we won't be needing that last
- * localdir allocation after all.
- */
- kmm_free(lowerdir);
- /* Check if the user is stat'ing some "fake" node between the unionfs
- * root and the file system 1 root directory.
- */
- if (unionfs_ispartprefix(relpath, ui->ui_fs[0].um_prefix))
- {
- /* File system 1 offset includes this relpath. Make sure that only
- * one
- */
- fu->fu_ndx = 0;
- fu->fu_prefix[0] = true;
- fu->fu_prefix[1] = false;
- }
- /* If the directory was not found on either file system, then we have
- * failed to open this path on either file system.
- */
- else if (fu->fu_lower[1] == NULL && !fu->fu_prefix[1])
- {
- /* Neither of the two path file systems include this relpath */
- ret = -ENOENT;
- goto errout_with_relpath;
- }
- }
- /* Increment the number of open references and return success */
- ui->ui_nopen++;
- DEBUGASSERT(ui->ui_nopen > 0);
- unionfs_semgive(ui);
- return OK;
- errout_with_fs2open:
- ops = ui->ui_fs[1].um_node->u.i_mops;
- DEBUGASSERT(ops != NULL);
- if (ops->closedir != NULL)
- {
- ret = ops->closedir(um->um_node, fu->fu_lower[1]);
- }
- kmm_free(fu->fu_lower[1]);
- errout_with_relpath:
- if (fu->fu_relpath != NULL)
- {
- kmm_free(fu->fu_relpath);
- }
- errout_with_semaphore:
- unionfs_semgive(ui);
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_closedir
- ****************************************************************************/
- static int unionfs_closedir(FAR struct inode *mountpt,
- FAR struct fs_dirent_s *dir)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- FAR struct fs_unionfsdir_s *fu;
- int ret = OK;
- int i;
- finfo("mountpt=%p dir=%p\n", mountpt, dir);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- /* Get exclusive access to the file system data structures */
- ret = unionfs_semtake(ui, true);
- if (ret < 0)
- {
- return ret;
- }
- DEBUGASSERT(dir);
- fu = &dir->u.unionfs;
- /* Close both contained file systems */
- for (i = 0; i < 2; i++)
- {
- /* Was this file system opened? */
- if (fu->fu_lower[i] != NULL)
- {
- um = &ui->ui_fs[i];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the lower level closedir operation */
- if (ops->closedir != NULL)
- {
- ret = ops->closedir(um->um_node, fu->fu_lower[i]);
- }
- /* Free the lower dirent structure */
- kmm_free(fu->fu_lower[i]);
- }
- }
- /* Free any allocated path */
- if (fu->fu_relpath != NULL)
- {
- kmm_free(fu->fu_relpath);
- }
- fu->fu_ndx = 0;
- fu->fu_relpath = NULL;
- fu->fu_lower[0] = NULL;
- fu->fu_lower[1] = NULL;
- /* Decrement the count of open reference. If that count would go to zero
- * and if the file system has been unmounted, then destroy the file system
- * now.
- */
- if (--ui->ui_nopen <= 0 && ui->ui_unmounted)
- {
- unionfs_destroy(ui);
- }
- else
- {
- unionfs_semgive(ui);
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_readdir
- ****************************************************************************/
- static int unionfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- FAR struct unionfs_mountpt_s *um0;
- FAR const struct mountpt_operations *ops;
- FAR struct fs_unionfsdir_s *fu;
- FAR char *relpath;
- struct stat buf;
- bool duplicate;
- int ret = -ENOSYS;
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- DEBUGASSERT(dir);
- fu = &dir->u.unionfs;
- /* Check if we are at the end of the directory listing. */
- if (fu->fu_eod)
- {
- /* End of file and error conditions are not distinguishable
- * with readdir. Here we return -ENOENT to signal the end
- * of the directory.
- */
- return -ENOENT;
- }
- DEBUGASSERT(fu->fu_ndx == 0 || fu->fu_ndx == 1);
- um = &ui->ui_fs[fu->fu_ndx];
- /* Special case: If the open directory is a 'fake' node in the prefix on
- * one of the mounted file system, then we must also fake the return value.
- */
- if (fu->fu_prefix[fu->fu_ndx])
- {
- DEBUGASSERT(fu->fu_lower[fu->fu_ndx] == NULL && um->um_prefix != NULL);
- /* Copy the file system offset into the dirent structure.
- * REVISIT: This will not handle the case where the prefix contains
- * the '/' character the so the offset appears to be multiple
- * directories.
- */
- strncpy(dir->fd_dir.d_name, um->um_prefix, NAME_MAX);
- /* Describe this as a read only directory */
- dir->fd_dir.d_type = DTYPE_DIRECTORY;
- /* Increment the index to file system 2 (maybe) */
- if (fu->fu_ndx == 0 && (fu->fu_prefix[1] || fu->fu_lower[1] != NULL))
- {
- /* Yes.. set up to do file system 2 next time */
- fu->fu_ndx++;
- }
- else
- {
- /* No.. we are finished */
- fu->fu_eod = true;
- }
- return OK;
- }
- /* This is a normal, mediated file system readdir() */
- DEBUGASSERT(fu->fu_lower[fu->fu_ndx] != NULL);
- DEBUGASSERT(um->um_node != NULL && um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- finfo("fu_ndx: %d\n", fu->fu_ndx);
- /* Perform the lower level readdir operation */
- if (ops->readdir != NULL)
- {
- /* Loop if we discard duplicate directory entries in filey system 2 */
- do
- {
- /* Read the directory entry */
- ret = ops->readdir(um->um_node, fu->fu_lower[fu->fu_ndx]);
- /* Did the read operation fail because we reached the end of the
- * directory? In that case, the error would be -ENOENT. If we
- * hit the end-of-directory on file system, we need to seamlessly
- * move to the second file system (if there is one).
- */
- if (ret == -ENOENT && fu->fu_ndx == 0)
- {
- /* Special case: If the open directory is a 'fake' node in the
- * prefix on file system2, then we must also fake the return
- * value.
- */
- if (fu->fu_prefix[1])
- {
- DEBUGASSERT(fu->fu_lower[1] == NULL);
- /* Switch to the second file system */
- fu->fu_ndx = 1;
- um = &ui->ui_fs[1];
- DEBUGASSERT(um != NULL && um->um_prefix != NULL);
- /* Copy the file system offset into the dirent structure.
- * REVISIT: This will not handle the case where the prefix
- * contains the '/' character the so the offset appears to
- * be multiple directories.
- */
- strncpy(dir->fd_dir.d_name, um->um_prefix, NAME_MAX);
- /* Describe this as a read only directory */
- dir->fd_dir.d_type = DTYPE_DIRECTORY;
- /* Mark the end of the directory listing */
- fu->fu_eod = true;
- /* Check if have already reported something of this name
- * in file system 1.
- */
- relpath = unionfs_relpath(fu->fu_relpath, um->um_prefix);
- if (relpath)
- {
- int tmp;
- /* Check if anything exists at this path on file
- * system 1
- */
- um0 = &ui->ui_fs[0];
- tmp = unionfs_trystat(um0->um_node, relpath,
- um0->um_prefix, &buf);
- /* Free the allocated relpath */
- kmm_free(relpath);
- /* Check for a duplicate */
- if (tmp >= 0)
- {
- /* There is something there!
- * REVISIT: We could allow files and directories to
- * have duplicate names.
- */
- return -ENOENT;
- }
- }
- return OK;
- }
- /* No.. check for a normal directory access */
- else if (fu->fu_lower[1] != NULL)
- {
- /* Switch to the second file system */
- fu->fu_ndx = 1;
- um = &ui->ui_fs[1];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Make sure that the second file system directory
- * enumeration is rewound to the beginning of the
- * directory.
- */
- if (ops->rewinddir != NULL)
- {
- ret = ops->rewinddir(um->um_node, fu->fu_lower[1]);
- }
- /* Then try the read operation again */
- ret = ops->readdir(um->um_node, fu->fu_lower[1]);
- }
- }
- /* Did we successfully read a directory from file system 2? If
- * so, we need to omit an duplicates that should be occluded by
- * the matching file on file system 1 (if we are enumerating
- * file system 1).
- */
- duplicate = false;
- if (ret >= 0 && fu->fu_ndx == 1 && fu->fu_lower[0] != NULL)
- {
- /* Get the relative path to the same file on file system 1.
- * NOTE: the on any failures we just assume that the filep
- * is not a duplicate.
- */
- relpath = unionfs_relpath(fu->fu_relpath,
- fu->fu_lower[1]->fd_dir.d_name);
- if (relpath)
- {
- int tmp;
- /* Check if anything exists at this path on file system 1 */
- um0 = &ui->ui_fs[0];
- tmp = unionfs_trystat(um0->um_node, relpath,
- um0->um_prefix, &buf);
- if (tmp >= 0)
- {
- /* There is something there!
- * REVISIT: We could allow files and directories to
- * have duplicate names.
- */
- duplicate = true;
- }
- /* Free the allocated relpath */
- kmm_free(relpath);
- }
- }
- }
- while (duplicate);
- /* Copy the return information into the dirent structure that the
- * application will see.
- */
- dir->fd_position = fu->fu_lower[fu->fu_ndx]->fd_position;
- memcpy(&dir->fd_dir, &fu->fu_lower[fu->fu_ndx]->fd_dir,
- sizeof(struct dirent));
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_rewindir
- ****************************************************************************/
- static int unionfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- FAR const struct mountpt_operations *ops;
- FAR struct fs_unionfsdir_s *fu;
- int ret = -EINVAL;
- finfo("mountpt=%p dir=%p\n", mountpt, dir);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- DEBUGASSERT(dir);
- fu = &dir->u.unionfs;
- /* Were we currently enumerating on file system 1? If not, is an
- * enumeration possible on file system 1?
- */
- DEBUGASSERT(fu->fu_ndx == 0 || fu->fu_ndx == 1);
- if (/* fu->fu_ndx != 0 && */ fu->fu_prefix[0] || fu->fu_lower[0] != NULL)
- {
- /* Yes.. switch to file system 1 */
- fu->fu_ndx = 0;
- }
- if (!fu->fu_prefix[fu->fu_ndx])
- {
- DEBUGASSERT(fu->fu_lower[fu->fu_ndx] != NULL);
- um = &ui->ui_fs[fu->fu_ndx];
- DEBUGASSERT(um != NULL && um->um_node != NULL &&
- um->um_node->u.i_mops != NULL);
- ops = um->um_node->u.i_mops;
- /* Perform the file system rewind operation */
- if (ops->rewinddir != NULL)
- {
- ret = ops->rewinddir(um->um_node, fu->fu_lower[fu->fu_ndx]);
- dir->fd_position = fu->fu_lower[fu->fu_ndx]->fd_position;
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_bind
- ****************************************************************************/
- static int unionfs_bind(FAR struct inode *blkdriver, FAR const void *data,
- FAR void **handle)
- {
- FAR const char *fspath1 = "";
- FAR const char *prefix1 = "";
- FAR const char *fspath2 = "";
- FAR const char *prefix2 = "";
- FAR char *dup;
- FAR char *tmp;
- FAR char *tok;
- int ret;
- /* Parse options from mount syscall */
- dup = tmp = strdup(data);
- if (!dup)
- {
- return -ENOMEM;
- }
- while ((tok = strsep(&tmp, ",")))
- {
- if (tok == strstr(tok, "fspath1="))
- {
- fspath1 = tok + 8;
- }
- else if (tok == strstr(tok, "prefix1="))
- {
- prefix1 = tok + 8;
- }
- else if (tok == strstr(tok, "fspath2="))
- {
- fspath2 = tok + 8;
- }
- else if (tok == strstr(tok, "prefix2="))
- {
- prefix2 = tok + 8;
- }
- }
- /* Call unionfs_dobind to do the real work. */
- ret = unionfs_dobind(fspath1, prefix1, fspath2, prefix2, handle);
- kmm_free(dup);
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_unbind
- ****************************************************************************/
- static int unionfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
- unsigned int flags)
- {
- FAR struct unionfs_inode_s *ui;
- int ret;
- finfo("handle=%p blkdriver=%p flags=%x\n", handle, blkdriver, flags);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(handle != NULL);
- ui = (FAR struct unionfs_inode_s *)handle;
- /* Get exclusive access to the file system data structures */
- ret = unionfs_semtake(ui, true);
- if (ret < 0)
- {
- return ret;
- }
- /* Mark the file system as unmounted. */
- ui->ui_unmounted = true;
- /* If there are no open references, then we can destroy the file system
- * now.
- */
- if (ui->ui_nopen <= 0)
- {
- unionfs_semgive(ui);
- unionfs_destroy(ui);
- }
- else
- {
- unionfs_semgive(ui);
- }
- return OK;
- }
- /****************************************************************************
- * Name: unionfs_statfs
- ****************************************************************************/
- static int unionfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um1;
- FAR struct unionfs_mountpt_s *um2;
- FAR const struct mountpt_operations *ops1;
- FAR const struct mountpt_operations *ops2;
- FAR struct statfs *adj;
- struct statfs buf1;
- struct statfs buf2;
- uint64_t tmp;
- uint32_t ratiob16;
- int ret;
- finfo("mountpt=%p buf=%p\n", mountpt, buf);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && buf != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- memset(buf, 0, sizeof(struct statfs));
- /* Get statfs info from file system 1.
- *
- * REVISIT: What would it mean if one file system did not support statfs?
- * Perhaps we could simplify the following by simply insisting that both
- * file systems support the statfs method.
- */
- um1 = &ui->ui_fs[0];
- DEBUGASSERT(um1 != NULL && um1->um_node != NULL &&
- um1->um_node->u.i_mops != NULL);
- ops1 = um1->um_node->u.i_mops;
- um2 = &ui->ui_fs[1];
- DEBUGASSERT(um2 != NULL && um2->um_node != NULL &&
- um2->um_node->u.i_mops != NULL);
- ops2 = um2->um_node->u.i_mops;
- if (ops1->statfs != NULL && ops2->statfs != NULL)
- {
- ret = ops1->statfs(um1->um_node, &buf1);
- if (ret < 0)
- {
- return ret;
- }
- /* Get stafs info from file system 2 */
- ret = ops2->statfs(um2->um_node, &buf2);
- if (ret < 0)
- {
- return ret;
- }
- }
- else if (ops1->statfs != NULL)
- {
- /* We have statfs for file system 1 only */
- return ops1->statfs(um1->um_node, buf);
- }
- else if (ops2->statfs != NULL)
- {
- /* We have statfs for file system 2 only */
- return ops2->statfs(um2->um_node, buf);
- }
- else
- {
- /* We could not get stafs info from either file system */
- return -ENOSYS;
- }
- /* We get here is we successfully obtained statfs info from both file
- * systems. Now combine those results into one statfs report, trying to
- * reconcile any conflicts between the file system geometries.
- */
- buf->f_type = UNIONFS_MAGIC;
- buf->f_namelen = MIN(buf1.f_namelen, buf2.f_namelen);
- buf->f_files = buf1.f_files + buf2.f_files;
- buf->f_ffree = buf1.f_ffree + buf2.f_ffree;
- /* Things expressed in units of blocks are the only tricky ones. We will
- * depend on a uint64_t * temporary to avoid arithmetic overflow.
- */
- buf->f_bsize = buf1.f_bsize;
- if (buf1.f_bsize != buf2.f_bsize)
- {
- if (buf1.f_bsize < buf2.f_bsize)
- {
- tmp = (((uint64_t)buf2.f_blocks *
- (uint64_t)buf2.f_bsize) << 16);
- ratiob16 = (uint32_t)(tmp / buf1.f_bsize);
- adj = &buf2;
- }
- else
- {
- buf->f_bsize = buf2.f_bsize;
- tmp = (((uint64_t)buf1.f_blocks *
- (uint64_t)buf1.f_bsize) << 16);
- ratiob16 = (uint32_t)(tmp / buf2.f_bsize);
- adj = &buf1;
- }
- tmp = (uint64_t)adj->f_blocks * ratiob16;
- adj->f_blocks = (off_t)(tmp >> 16);
- tmp = (uint64_t)adj->f_bfree * ratiob16;
- adj->f_bfree = (off_t)(tmp >> 16);
- tmp = (uint64_t)adj->f_bavail * ratiob16;
- adj->f_bavail = (off_t)(tmp >> 16);
- }
- /* Then we can just sum the adjusted sizes */
- buf->f_blocks = buf1.f_blocks + buf2.f_blocks;
- buf->f_bfree = buf1.f_bfree + buf2.f_bfree;
- buf->f_bavail = buf1.f_bavail + buf2.f_bavail;
- return OK;
- }
- /****************************************************************************
- * Name: unionfs_unlink
- ****************************************************************************/
- static int unionfs_unlink(FAR struct inode *mountpt,
- FAR const char *relpath)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- struct stat buf;
- int ret;
- finfo("relpath: %s\n", relpath);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL &&
- relpath != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- /* Check if some exists at this path on file system 1. This might be
- * a file or a directory
- */
- um = &ui->ui_fs[0];
- ret = unionfs_trystat(um->um_node, relpath, um->um_prefix, &buf);
- if (ret >= 0)
- {
- /* Yes.. Try to unlink the file on file system 1 (perhaps exposing
- * a file of the same name on file system 2). This would fail
- * with -ENOSYS if file system 1 is a read-only only file system or
- * -EISDIR if the path is not a file.
- */
- ret = unionfs_tryunlink(um->um_node, relpath, um->um_prefix);
- }
- /* There is nothing at this path on file system 1 */
- else
- {
- /* Check if the file exists with name on file system 2. The only
- * reason that we check here is so that we can return the more
- * meaningful -ENOSYS if file system 2 is a read-only file system.
- */
- um = &ui->ui_fs[1];
- ret = unionfs_trystat(um->um_node, relpath, um->um_prefix, &buf);
- if (ret >= 0)
- {
- /* Yes.. Try to unlink the file on file system 1. This would fail
- * with -ENOSYS if file system 2 is a read-only only file system or
- * -EISDIR if the path is not a file.
- * */
- ret = unionfs_tryunlink(um->um_node, relpath, um->um_prefix);
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_mkdir
- ****************************************************************************/
- static int unionfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath,
- mode_t mode)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- struct stat buf;
- int ret1;
- int ret2;
- int ret;
- finfo("relpath: %s\n", relpath);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL &&
- relpath != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- /* Is there anything with this name on either file system? */
- um = &ui->ui_fs[0];
- ret = unionfs_trystat(um->um_node, relpath, um->um_prefix, &buf);
- if (ret >= 0)
- {
- return -EEXIST;
- }
- um = &ui->ui_fs[1];
- ret = unionfs_trystat(um->um_node, relpath, um->um_prefix, &buf);
- if (ret >= 0)
- {
- return -EEXIST;
- }
- /* Try to create the directory on both file systems. */
- um = &ui->ui_fs[0];
- ret1 = unionfs_trymkdir(um->um_node, relpath, um->um_prefix, mode);
- um = &ui->ui_fs[1];
- ret2 = unionfs_trymkdir(um->um_node, relpath, um->um_prefix, mode);
- /* We will say we were successful if we were able to create the
- * directory on either file system. Perhaps one file system is
- * read-only and the other is write-able?
- */
- return (ret1 >= 0 || ret2 >= 0) ? OK : ret1;
- }
- /****************************************************************************
- * Name: unionfs_rmdir
- ****************************************************************************/
- static int unionfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- int ret = -ENOENT;
- int tmp;
- finfo("relpath: %s\n", relpath);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL &&
- relpath != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- /* We really don't know any better so we will try to remove the directory
- * from both file systems.
- */
- /* Is there a directory with this name on file system 1 */
- um = &ui->ui_fs[0];
- tmp = unionfs_trystatdir(um->um_node, relpath, um->um_prefix);
- if (tmp >= 0)
- {
- /* Yes.. remove it. Since we know that the directory exists, any
- * failure to remove it is a showstopper.
- */
- ret = unionfs_tryrmdir(um->um_node, relpath, um->um_prefix);
- if (ret < 0)
- {
- return ret;
- }
- }
- /* Either the directory does not exist on file system 1, or we
- * successfully removed it. Try again on file system 2.
- */
- um = &ui->ui_fs[1];
- tmp = unionfs_trystatdir(um->um_node, relpath, um->um_prefix);
- if (tmp >= 0)
- {
- /* Yes.. remove it. Since we know that the directory exists, any
- * failure to remove it is a showstopper.
- */
- ret = unionfs_tryrmdir(um->um_node, relpath, um->um_prefix);
- /* REVISIT: Should we try to restore the directory on file system 1
- * if we failure to removed the directory on file system 2?
- */
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_rename
- ****************************************************************************/
- static int unionfs_rename(FAR struct inode *mountpt,
- FAR const char *oldrelpath,
- FAR const char *newrelpath)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- int ret = -ENOENT;
- int tmp;
- finfo("oldrelpath: %s newrelpath: %s\n", oldrelpath, newrelpath);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- DEBUGASSERT(oldrelpath != NULL && oldrelpath != NULL);
- /* Is there a file with this name on file system 1 */
- um = &ui->ui_fs[0];
- tmp = unionfs_trystatfile(um->um_node, oldrelpath, um->um_prefix);
- if (tmp >= 0)
- {
- /* Yes.. rename it. Since we know that the directory exists, any
- * failure to remove it is a showstopper.
- */
- ret = unionfs_tryrename(um->um_node, oldrelpath, newrelpath,
- um->um_prefix);
- if (ret >= 0)
- {
- /* Return immediately on success. In the event that the file
- * exists in both file systems, this will produce the odd behavior
- * that one file on file system 1 was renamed but another obscured
- * file of the same relative path will become visible.
- */
- return OK;
- }
- }
- /* Either the file does not exist on file system 1, or we failed to rename
- * it (perhaps because the file system was read-only). Try again on file
- * system 2.
- */
- um = &ui->ui_fs[1];
- tmp = unionfs_trystatfile(um->um_node, oldrelpath, um->um_prefix);
- if (tmp >= 0)
- {
- /* Yes.. remove it. Since we know that the directory exists, any
- * failure to remove it is a showstopper.
- */
- ret = unionfs_tryrename(um->um_node, oldrelpath, newrelpath,
- um->um_prefix);
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_stat
- ****************************************************************************/
- static int unionfs_stat(FAR struct inode *mountpt, FAR const char *relpath,
- FAR struct stat *buf)
- {
- FAR struct unionfs_inode_s *ui;
- FAR struct unionfs_mountpt_s *um;
- int ret;
- finfo("relpath: %s\n", relpath);
- /* Recover the union file system data from the struct inode instance */
- DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL &&
- relpath != NULL);
- ui = (FAR struct unionfs_inode_s *)mountpt->i_private;
- /* stat this path on file system 1 */
- um = &ui->ui_fs[0];
- ret = unionfs_trystat(um->um_node, relpath, um->um_prefix, buf);
- if (ret >= 0)
- {
- /* Return on the first success. The first instance of the file will
- * shadow the second anyway.
- */
- return OK;
- }
- /* stat failed on the file system 1. Try again on file system 2. */
- um = &ui->ui_fs[1];
- ret = unionfs_trystat(um->um_node, relpath, um->um_prefix, buf);
- if (ret >= 0)
- {
- /* Return on the first success. The first instance of the file will
- * shadow the second anyway.
- */
- return OK;
- }
- /* Special case the unionfs root directory when both file systems are
- * offset. In that case, both of the above trystat calls will fail.
- */
- if (ui->ui_fs[0].um_prefix != NULL && ui->ui_fs[1].um_prefix != NULL)
- {
- /* Most of the stat entries just do not apply */
- memset(buf, 0, sizeof(struct stat));
- /* Claim that this is a read-only directory */
- buf->st_mode = S_IFDIR | S_IROTH | S_IRGRP | S_IRUSR;
- /* Check if the user is stat'ing some "fake" node between the unionfs
- * root and the file system 1 root directory.
- */
- if (unionfs_ispartprefix(relpath, ui->ui_fs[0].um_prefix) ||
- unionfs_ispartprefix(relpath, ui->ui_fs[1].um_prefix))
- {
- ret = OK;
- }
- else
- {
- ret = -ENOENT;
- }
- }
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_getmount
- ****************************************************************************/
- static int unionfs_getmount(FAR const char *path, FAR struct inode **inode)
- {
- FAR struct inode *minode;
- struct inode_search_s desc;
- int ret;
- /* Find the mountpt */
- SETUP_SEARCH(&desc, path, false);
- ret = inode_find(&desc);
- if (ret < 0)
- {
- /* Mountpoint inode not found */
- goto errout_with_search;
- }
- /* Get the search results */
- minode = desc.node;
- DEBUGASSERT(minode != NULL);
- /* Verify that the inode is a mountpoint.
- *
- * REVISIT: If desc.relpath points to a non-empty string, then the path
- * does not really refer to a mountpoint but, rather, to a some entity
- * within the mounted volume.
- */
- if (!INODE_IS_MOUNTPT(minode))
- {
- /* Inode was found, but is it is not a mounpoint */
- ret = -EINVAL;
- goto errout_with_inode;
- }
- /* Success! */
- *inode = minode;
- RELEASE_SEARCH(&desc);
- return OK;
- errout_with_inode:
- inode_release(minode);
- errout_with_search:
- RELEASE_SEARCH(&desc);
- return ret;
- }
- /****************************************************************************
- * Name: unionfs_dobind
- ****************************************************************************/
- static int unionfs_dobind(FAR const char *fspath1, FAR const char *prefix1,
- FAR const char *fspath2, FAR const char *prefix2,
- FAR void **handle)
- {
- FAR struct unionfs_inode_s *ui;
- int ret;
- DEBUGASSERT(fspath1 != NULL && fspath2 != NULL && handle != NULL);
- /* Allocate a structure a structure that will describe the union file
- * system.
- */
- ui = (FAR struct unionfs_inode_s *)
- kmm_zalloc(sizeof(struct unionfs_inode_s));
- if (!ui)
- {
- ferr("ERROR: Failed to allocated union FS state structure\n");
- return -ENOMEM;
- }
- nxsem_init(&ui->ui_exclsem, 0, 1);
- /* Get the inodes associated with fspath1 and fspath2 */
- ret = unionfs_getmount(fspath1, &ui->ui_fs[0].um_node);
- if (ret < 0)
- {
- ferr("ERROR: unionfs_getmount(fspath1) failed: %d\n", ret);
- goto errout_with_uinode;
- }
- ret = unionfs_getmount(fspath2, &ui->ui_fs[1].um_node);
- if (ret < 0)
- {
- ferr("ERROR: unionfs_getmount(fspath2) failed: %d\n", ret);
- goto errout_with_fs1;
- }
- /* Duplicate the prefix strings */
- if (prefix1 && strlen(prefix1) > 0)
- {
- ui->ui_fs[0].um_prefix = strdup(prefix1);
- if (ui->ui_fs[0].um_prefix == NULL)
- {
- ferr("ERROR: strdup(prefix1) failed\n");
- ret = -ENOMEM;
- goto errout_with_fs2;
- }
- }
- if (prefix2 && strlen(prefix2) > 0)
- {
- ui->ui_fs[1].um_prefix = strdup(prefix2);
- if (ui->ui_fs[1].um_prefix == NULL)
- {
- ferr("ERROR: strdup(prefix2) failed\n");
- ret = -ENOMEM;
- goto errout_with_prefix1;
- }
- }
- /* Unlink the contained mountpoint inodes from the pseudo file system.
- * The inodes will be marked as deleted so that they will be removed when
- * the reference count decrements to zero in inode_release(). Because we
- * hold a reference count on the inodes, they will not be deleted until
- * this logic calls inode_release() in the unionfs_destroy() function.
- */
- inode_remove(fspath1);
- inode_remove(fspath2);
- *handle = ui;
- return OK;
- errout_with_prefix1:
- if (ui->ui_fs[0].um_prefix != NULL)
- {
- kmm_free(ui->ui_fs[0].um_prefix);
- }
- errout_with_fs2:
- inode_release(ui->ui_fs[1].um_node);
- errout_with_fs1:
- inode_release(ui->ui_fs[0].um_node);
- errout_with_uinode:
- nxsem_destroy(&ui->ui_exclsem);
- kmm_free(ui);
- return ret;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: unionfs_mount
- *
- * Description:
- * Create and mount a union file system
- *
- * Input Parameters:
- * fspath1 - The full path to the first file system mountpoint
- * prefix1 - An optiona prefix that may be applied to make the first
- * file system appear a some path below the unionfs mountpoint,
- * fspath2 - The full path to the second file system mountpoint
- * prefix2 - An optiona prefix that may be applied to make the first
- * file system appear a some path below the unionfs mountpoint,
- * mountpt - The full path to the mountpoint for the union file system
- *
- * Returned Value:
- * Zero (OK) is returned if the union file system was correctly created and
- * mounted. On any failure, a negated error value will be returned to
- * indicate the nature of the failure.
- *
- ****************************************************************************/
- int unionfs_mount(FAR const char *fspath1, FAR const char *prefix1,
- FAR const char *fspath2, FAR const char *prefix2,
- FAR const char *mountpt)
- {
- FAR struct inode *mpinode;
- int ret;
- DEBUGASSERT(mountpt != NULL);
- /* Mount the union FS. We should adapt the standard mount to do
- * this using optional parameters. This custom mount should do the job
- * for now, however.
- */
- ret = inode_reserve(mountpt, &mpinode);
- if (ret < 0)
- {
- /* inode_reserve can fail for a couple of reasons, but the most likely
- * one is that the inode already exists. inode_reserve may return:
- *
- * -EINVAL - 'path' is invalid for this operation
- * -EEXIST - An inode already exists at 'path'
- * -ENOMEM - Failed to allocate in-memory resources for the operation
- */
- ferr("ERROR: Failed to reserve inode\n");
- return ret;
- }
- /* Populate the inode with driver specific information. */
- INODE_SET_MOUNTPT(mpinode);
- mpinode->u.i_mops = &unionfs_operations;
- #ifdef CONFIG_FILE_MODE
- mpinode->i_mode = 0755;
- #endif
- /* Call unionfs_dobind to do the real work. */
- ret = unionfs_dobind(fspath1, prefix1, fspath2, prefix2,
- &mpinode->i_private);
- if (ret < 0)
- {
- goto errout_with_mountpt;
- }
- return OK;
- errout_with_mountpt:
- inode_release(mpinode);
- return ret;
- }
- #endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_UNIONFS */
|